Skip to content

Add interposer extensions for LBE,TSV,pin and forbidden - #19

Open
yegappan-ihp wants to merge 4 commits into
devfrom
feature/interposer-extensions
Open

Add interposer extensions for LBE,TSV,pin and forbidden#19
yegappan-ihp wants to merge 4 commits into
devfrom
feature/interposer-extensions

Conversation

@yegappan-ihp

Copy link
Copy Markdown

Added additional rules for interposer with respect to the recent discussions.Made changes compatible to accommodate the new rule in the run DRC file as well.Ran simple smoke tests to detect the changes in the added rule sets.Testcases are also updated.

@Mauricio-xx Mauricio-xx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR. The pin and forbidden decks are clean ports, the rule-name slicing for M4/M5/TM1/TM2 is exactly right, and LBE.f closes a gap we had left open. I ran the whole thing against a few fixtures of my own and found two things in the TSV_G half that I think have to change before it lands, plus a handful of smaller asks.

Since TSV is on our roadmap for the interposer, this table isn't speculative and the deck belongs here, which is precisely why I'd like these two fixed now rather than after people start drawing DeepVia.

Everything below is reproducible with the fixture generator at the bottom of this comment (KLayout 0.30.5). The fixture is a TSV built from the device geometry (outer 25.0 um, wall 3.0 um, hole 19.0 um) with its enclosures (PWell:block at outer+2.5, Metal1 at outer+1.5), sitting in an 800x800 um prBoundary.

Blocking

A conforming TSV can't pass a default DRC run

$ run_drc.py --path tsv_one_conforming.gds --topcell tsv_one_conforming
Violated rules: ['TSV_G.d', 'deepvia_drw_Acute']

TSV_G.a/.b/.c/.e/.f/.g are all clean on that shape, so the deck itself agrees it conforms. Two rules from two different decks still reject it, and there's no geometry that clears both.

TSV_G.d measures each ring against its own opening

10_1_tsv_g.drc:57 runs deepvia_ring.space(...) on the annulus, which still carries its hole, and Region#space is notch ∪ isolated, so a polygon's own hole counts. The split makes it obvious:

fixture space(25) notch(25) isolated(25)
one conforming ring 992 992 0
two rings, 35 um apart (legal) 1984 1984 0
two rings, 10 um apart (illegal) 2412 1984 428

1984 is exactly 2 x 992, i.e. zero inter-ring contribution when the rings are legally spaced. There's also an internal contradiction worth pointing at: line 36 derives the mandated inner diameter as TSV_G_c - 2*TSV_G_b = 19.0 and TSV_G.b rejects anything else, while TSV_G.d rejects 19.0. Nothing can satisfy both.

Once you've had a look, the check I'd use is that a lone conforming ring and two conforming rings 35 um apart both give zero TSV_G.d markers, while two rings 10 um apart still fail. tsv_one_conforming, tsv_pair_legal and tsv_pair_too_close from the generator below are those three cases.

deepvia_drw_Acute fires on every conforming ring

This one comes from the 3-line hunk in 3_2_angle.drc:116-118. Putting deepvia_drw in ang_acute_scope exposes DeepVia to a check whose circle exemption, get_circle (intm4tm2.drc:86-93), is built for solid discs: it applies .without_holes and with_area_ratio(1.270, 1.276), i.e. the 4/pi bbox-to-area ratio of a filled circle. An annulus comes out at 3.01 and never gets exempted. On top of that the rule uses .raw, so the keyhole seam GDS uses to store a donut survives as a degenerate corner.

--deck angle, solid 25 um disc  ->  DRC PASSED: No violations detected
--deck angle, conforming ring   ->  Violated rules: ['deepvia_drw_Acute']   (2 markers)

So the exemption works for the one DeepVia shape TSV_G.a forbids, and fails for the only one it allows. What I'd look for afterwards is a conforming ring passing --deck angle, without the exemption growing so wide that a DeepVia with a genuine acute corner slips through too.

The smoke suite can't tell a violation from a deck that never ran

run_interposer_smoke.py:55 only compares subprocess.run(...).returncode against expect_exit, and run_drc.py returns 1 for violations and for every error path alike. I swapped topmetal2_pin for an undefined name in 7_4_pin.drc and got the full CalledProcessError traceback followed by Executed 1 interposer smoke case(s) successfully. and exit 0. Same result if I delete a fixture, or delete all but one rule from 3_2_forbidden.drc.

I think this is the actual reason the two above slipped through: nothing in the PR can observe which rules fired. run_regression.py already compares violated-rule sets per top cell, so the machinery is there. Ideally the suite fails when a deck aborts and when a rule stops emitting its markers; those four mutations are a decent check.

Smaller things, before merge

  1. Each new fixture could use a *_clean top cell. All 16 existing fixtures ship a <table>_clean / <table>_viol pair and testing/README.md asks for it. Right now nothing asserts that legal geometry passes, which is how the two blockers got through.
  2. pin_violation and forbidden_violation are missing topcell in interposer_smoke_tests.json. They lean on single-top-cell auto-detection, so as soon as a _clean cell exists the runner aborts with "multiple top cells", and because of the point above the case still reports success.
  3. LBE.f never actually executes. lbe.gds only has 157/0, 41/0, 9/0 and 63/0, no Activ 1/0, so the second operand is empty by construction and GOLDEN['lbe'] is unchanged. lbe already has a generator and a golden entry, so of all the new rules this is the cheapest one to pin down.
  4. TSV_G.i and TSV_G.j are global density rules sitting on the default path. They normalise against CHIP (the layout extent), while every other chip-normalised rule uses density.drc's prBoundary > EdgeSeal-boundary > extent selection (density.drc:54-71), and DEFAULT_SKIP_DECKS is still {'density'} (intm4tm2.drc:155, run_drc.py:39). LBE.i is the precedent here: it belongs to spec table 9.1 but lives in density.drc. TSV_G.a through TSV_G.g should stay default-on regardless.
  5. It'd be good to keep the JSON provenance checkable. The old description listed the keys with no upstream JSON counterpart; it's now "approved SG13G2-derived interposer extensions", in the same commit that adds eight more such keys, and there's no changelog for the 1.4 to 1.5 bump. drc/docs/extra_rules.md covers TSV_G.a/d/f/g/i/j, but TSV_G_b, TSV_G_c and TSV_G_e don't have a source I can follow. A line in the deck header would do it.
  6. libs.tech/klayout/README.md lists the rule-deck files and a deck table in two places, and both still say 20 decks.
  7. DeepVia 152/0 isn't in intm4tm2.lyp. It's the primary input of the whole new table, and 24 of the 25 new layers_def.drc declarations are missing from the technology layer set, so today someone can be told their DeepVia violates nine rules and still not be able to display or select the layer. With TSV coming, DeepVia needs to become drawable, but that's a change to the layer set rather than to this PR and it's on us; I'm flagging it here so the deck and the layer set don't drift apart quietly. The other 23 are check-only inputs and it'd help if layers_def.drc said so.

One question

deepvia_drw went into 3_2_angle.drc's scope but not 3_1_offgrid.drc's. Was that deliberate? testcases/interposer/tsv.gds has 116 vertices off the 5 nm grid on 152/0, and with TSV in scope the grid check will start mattering for real geometry.

Not blocking, just noting

  • interacting(deepvia_drw.holes) picks polygons that touch a hole rather than ones that have one, so TSV_G.a misses a solid DeepVia sitting inside another ring's opening. Same idiom is already in 9_1_lbe.drc:66 though, so this is ours as much as yours.
  • TSV_G.b tests the hole's bounding box, which pins the mean wall but not concentricity: a displaced hole keeps a 19.000 x 19.000 extent while one wall thins out. Under-detection only, so not urgent.
  • Five comments in files you didn't touch (6_10_sealring.drc:29, 6_9_pad.drc:71, density.drc:29 and :352, 7_3_metalslits.drc:67) still say Activ/pSD/GatPoly/Cont/Metal1 don't exist in this stack. You already fixed the sixth one in 9_1_lbe.drc.
  • LBE.b2 .um to .um2 reads better, though it isn't a behaviour change (30000.um == 30000.um2 == 30000.0). LBE.b1 on line 46 still uses .um for an area if you feel like being consistent.
  • 10_1_tsv_g.drc:93-104 is indented a level deeper than the rest of the deck, and tsvg_bbox = nil on line 103 is dead.

Fixtures and probe used above (no dependency on the repo, save and run)

mk_tsv_fixtures.py, run with any Python that has the klayout module:

#!/usr/bin/env python3
"""Minimal fixtures for reviewing the TSV_G deck.

Geometry comes from the TSV device: outer radius 12.5 um, inner radius 9.5 um,
so outer diameter 25.0 = TSV_G_c, wall 3.0 = TSV_G_b, hole 19.0. Enclosures as
the device draws them: PWell:block at outer+2.5 (TSV_G_f), Metal1 at outer+1.5
(TSV_G_g). Every cell is a conforming TSV unless its name says otherwise.
"""
import math
import sys

import klayout.db as db

DBU = 0.001
R_OUT = 12.5
R_IN = 9.5
NSIDES = 64          # matches a polygonal-circle donut
PRB = 400.0          # prBoundary half-size: keeps the DeepVia density rules quiet


def ring_pts(r, n=NSIDES):
    """Circle approximation with a vertex on +x, so bbox == 2*r exactly."""
    return [db.DPoint(r * math.cos(2 * math.pi * k / n),
                      r * math.sin(2 * math.pi * k / n)) for k in range(n)]


def donut(cx=0.0, cy=0.0):
    p = db.DPolygon(ring_pts(R_OUT))
    p.insert_hole(ring_pts(R_IN))
    return p.moved(cx, cy)


def disc(cx=0.0, cy=0.0):
    return db.DPolygon(ring_pts(R_OUT)).moved(cx, cy)


def square_ring(cx=0.0, cy=0.0):
    p = db.DPolygon(db.DBox(-R_OUT, -R_OUT, R_OUT, R_OUT))
    p.insert_hole(db.DBox(-R_IN, -R_IN, R_IN, R_IN))
    return p.moved(cx, cy)


def new_layout(cell_name):
    ly = db.Layout()
    ly.dbu = DBU
    top = ly.create_cell(cell_name)
    lay = {
        "deepvia": ly.layer(152, 0),
        "pwell_block": ly.layer(46, 21),
        "metal1": ly.layer(8, 0),
        "prboundary": ly.layer(235, 0),
    }
    top.shapes(lay["prboundary"]).insert(db.DBox(-PRB, -PRB, PRB, PRB))
    return ly, top, lay


def add_context(top, lay, cx=0.0, cy=0.0):
    """Enclosures a conforming TSV carries: TSV_G_f = 2.5, TSV_G_g = 1.5."""
    top.shapes(lay["pwell_block"]).insert(
        db.DBox(cx - R_OUT - 2.5, cy - R_OUT - 2.5, cx + R_OUT + 2.5, cy + R_OUT + 2.5))
    top.shapes(lay["metal1"]).insert(
        db.DBox(cx - R_OUT - 1.5, cy - R_OUT - 1.5, cx + R_OUT + 1.5, cy + R_OUT + 1.5))


def write(outdir, name, build):
    ly, top, lay = new_layout(name)
    build(top, lay)
    ly.write("%s/%s.gds" % (outdir, name))
    print("wrote %s/%s.gds  (topcell %s)" % (outdir, name, name))


def main():
    outdir = sys.argv[1] if len(sys.argv) > 1 else "."

    # One conforming TSV, alone, with its enclosures. Should be clean.
    def f_one(top, lay):
        top.shapes(lay["deepvia"]).insert(donut())
        add_context(top, lay)
    write(outdir, "tsv_one_conforming", f_one)

    # Same as an axis-aligned square annulus: on the 5 nm grid, and it isolates
    # TSV_G.d from any circle-discretisation effect.
    def f_sq(top, lay):
        top.shapes(lay["deepvia"]).insert(square_ring())
        add_context(top, lay)
    write(outdir, "tsv_one_square", f_sq)

    # Control for the angle-deck circle exemption: a SOLID disc, i.e. the one
    # DeepVia shape TSV_G.a forbids.
    def f_disc(top, lay):
        top.shapes(lay["deepvia"]).insert(disc())
    write(outdir, "tsv_solid_disc", f_disc)

    # Two conforming TSVs, 35 um edge to edge (legal: TSV_G_d = 25.0).
    def f_far(top, lay):
        for cx in (0.0, 60.0):
            top.shapes(lay["deepvia"]).insert(donut(cx))
            add_context(top, lay, cx)
    write(outdir, "tsv_pair_legal", f_far)

    # Two conforming TSVs, 10 um edge to edge (genuine TSV_G.d violation).
    def f_near(top, lay):
        for cx in (0.0, 35.0):
            top.shapes(lay["deepvia"]).insert(donut(cx))
            add_context(top, lay, cx)
    write(outdir, "tsv_pair_too_close", f_near)


if __name__ == "__main__":
    main()

Commands, from libs.tech/klayout/tech/drc on this branch:

python3 mk_tsv_fixtures.py /tmp/tsvfix

python3 run_drc.py --path /tmp/tsvfix/tsv_one_conforming.gds --topcell tsv_one_conforming --threads 1
python3 run_drc.py --path /tmp/tsvfix/tsv_solid_disc.gds     --topcell tsv_solid_disc     --deck angle  --threads 1
python3 run_drc.py --path /tmp/tsvfix/tsv_one_conforming.gds --topcell tsv_one_conforming --deck angle  --threads 1
python3 run_drc.py --path /tmp/tsvfix/tsv_one_square.gds     --topcell tsv_one_square     --deck tsv_g  --threads 1
python3 run_drc.py --path /tmp/tsvfix/tsv_pair_legal.gds     --topcell tsv_pair_legal     --deck tsv_g  --threads 1
python3 run_drc.py --path /tmp/tsvfix/tsv_pair_too_close.gds --topcell tsv_pair_too_close --deck tsv_g  --threads 1

probe_space.drc, for the notch/isolated split in the table above. Run it with klayout -b -zz -r probe_space.drc -rd input=/tmp/tsvfix/tsv_one_conforming.gds:

# Shows that Region#space == notch (intra-polygon) + isolated (inter-polygon).
src = source($input)
dv  = src.polygons(152, 0)
ring = dv.interacting(dv.holes)
puts "#{$input.split('/').last}: space=#{ring.space(25.0.um, euclidian).count} " \
     "notch=#{ring.notch(25.0.um, euclidian).count} " \
     "isolated=#{ring.isolated(25.0.um, euclidian).count}"

@Mauricio-xx

Copy link
Copy Markdown
Collaborator

Thanks for this PR. The pin and forbidden decks are clean ports, the rule-name slicing for M4/M5/TM1/TM2 is exactly right, and LBE.f closes a gap we had left open. I ran the whole thing against a few fixtures of my own and found two things in the TSV_G half that I think have to change before it lands, plus a handful of smaller asks.

Since TSV is on our roadmap for the interposer, this table isn't speculative and the deck belongs here, which is precisely why I'd like these two fixed now rather than after people start drawing DeepVia.

Everything below is reproducible with the fixture generator at the bottom of this comment (KLayout 0.30.5). The fixture is a TSV built from the device geometry (outer 25.0 um, wall 3.0 um, hole 19.0 um) with its enclosures (PWell:block at outer+2.5, Metal1 at outer+1.5), sitting in an 800x800 um prBoundary.

Blocking

A conforming TSV can't pass a default DRC run

$ run_drc.py --path tsv_one_conforming.gds --topcell tsv_one_conforming
Violated rules: ['TSV_G.d', 'deepvia_drw_Acute']

TSV_G.a/.b/.c/.e/.f/.g are all clean on that shape, so the deck itself agrees it conforms. Two rules from two different decks still reject it, and there's no geometry that clears both.

TSV_G.d measures each ring against its own opening

10_1_tsv_g.drc:57 runs deepvia_ring.space(...) on the annulus, which still carries its hole, and Region#space is notch ∪ isolated, so a polygon's own hole counts. The split makes it obvious:

fixture space(25) notch(25) isolated(25)
one conforming ring 992 992 0
two rings, 35 um apart (legal) 1984 1984 0
two rings, 10 um apart (illegal) 2412 1984 428
1984 is exactly 2 x 992, i.e. zero inter-ring contribution when the rings are legally spaced. There's also an internal contradiction worth pointing at: line 36 derives the mandated inner diameter as TSV_G_c - 2*TSV_G_b = 19.0 and TSV_G.b rejects anything else, while TSV_G.d rejects 19.0. Nothing can satisfy both.

Once you've had a look, the check I'd use is that a lone conforming ring and two conforming rings 35 um apart both give zero TSV_G.d markers, while two rings 10 um apart still fail. tsv_one_conforming, tsv_pair_legal and tsv_pair_too_close from the generator below are those three cases.

deepvia_drw_Acute fires on every conforming ring

This one comes from the 3-line hunk in 3_2_angle.drc:116-118. Putting deepvia_drw in ang_acute_scope exposes DeepVia to a check whose circle exemption, get_circle (intm4tm2.drc:86-93), is built for solid discs: it applies .without_holes and with_area_ratio(1.270, 1.276), i.e. the 4/pi bbox-to-area ratio of a filled circle. An annulus comes out at 3.01 and never gets exempted. On top of that the rule uses .raw, so the keyhole seam GDS uses to store a donut survives as a degenerate corner.

--deck angle, solid 25 um disc  ->  DRC PASSED: No violations detected
--deck angle, conforming ring   ->  Violated rules: ['deepvia_drw_Acute']   (2 markers)

So the exemption works for the one DeepVia shape TSV_G.a forbids, and fails for the only one it allows. What I'd look for afterwards is a conforming ring passing --deck angle, without the exemption growing so wide that a DeepVia with a genuine acute corner slips through too.

The smoke suite can't tell a violation from a deck that never ran

run_interposer_smoke.py:55 only compares subprocess.run(...).returncode against expect_exit, and run_drc.py returns 1 for violations and for every error path alike. I swapped topmetal2_pin for an undefined name in 7_4_pin.drc and got the full CalledProcessError traceback followed by Executed 1 interposer smoke case(s) successfully. and exit 0. Same result if I delete a fixture, or delete all but one rule from 3_2_forbidden.drc.

I think this is the actual reason the two above slipped through: nothing in the PR can observe which rules fired. run_regression.py already compares violated-rule sets per top cell, so the machinery is there. Ideally the suite fails when a deck aborts and when a rule stops emitting its markers; those four mutations are a decent check.

Smaller things, before merge

  1. Each new fixture could use a *_clean top cell. All 16 existing fixtures ship a <table>_clean / <table>_viol pair and testing/README.md asks for it. Right now nothing asserts that legal geometry passes, which is how the two blockers got through.
  2. pin_violation and forbidden_violation are missing topcell in interposer_smoke_tests.json. They lean on single-top-cell auto-detection, so as soon as a _clean cell exists the runner aborts with "multiple top cells", and because of the point above the case still reports success.
  3. LBE.f never actually executes. lbe.gds only has 157/0, 41/0, 9/0 and 63/0, no Activ 1/0, so the second operand is empty by construction and GOLDEN['lbe'] is unchanged. lbe already has a generator and a golden entry, so of all the new rules this is the cheapest one to pin down.
  4. TSV_G.i and TSV_G.j are global density rules sitting on the default path. They normalise against CHIP (the layout extent), while every other chip-normalised rule uses density.drc's prBoundary > EdgeSeal-boundary > extent selection (density.drc:54-71), and DEFAULT_SKIP_DECKS is still {'density'} (intm4tm2.drc:155, run_drc.py:39). LBE.i is the precedent here: it belongs to spec table 9.1 but lives in density.drc. TSV_G.a through TSV_G.g should stay default-on regardless.
  5. It'd be good to keep the JSON provenance checkable. The old description listed the keys with no upstream JSON counterpart; it's now "approved SG13G2-derived interposer extensions", in the same commit that adds eight more such keys, and there's no changelog for the 1.4 to 1.5 bump. drc/docs/extra_rules.md covers TSV_G.a/d/f/g/i/j, but TSV_G_b, TSV_G_c and TSV_G_e don't have a source I can follow. A line in the deck header would do it.
  6. libs.tech/klayout/README.md lists the rule-deck files and a deck table in two places, and both still say 20 decks.
  7. DeepVia 152/0 isn't in intm4tm2.lyp. It's the primary input of the whole new table, and 24 of the 25 new layers_def.drc declarations are missing from the technology layer set, so today someone can be told their DeepVia violates nine rules and still not be able to display or select the layer. With TSV coming, DeepVia needs to become drawable, but that's a change to the layer set rather than to this PR and it's on us; I'm flagging it here so the deck and the layer set don't drift apart quietly. The other 23 are check-only inputs and it'd help if layers_def.drc said so.

One question

deepvia_drw went into 3_2_angle.drc's scope but not 3_1_offgrid.drc's. Was that deliberate? testcases/interposer/tsv.gds has 116 vertices off the 5 nm grid on 152/0, and with TSV in scope the grid check will start mattering for real geometry.

Not blocking, just noting

  • interacting(deepvia_drw.holes) picks polygons that touch a hole rather than ones that have one, so TSV_G.a misses a solid DeepVia sitting inside another ring's opening. Same idiom is already in 9_1_lbe.drc:66 though, so this is ours as much as yours.
  • TSV_G.b tests the hole's bounding box, which pins the mean wall but not concentricity: a displaced hole keeps a 19.000 x 19.000 extent while one wall thins out. Under-detection only, so not urgent.
  • Five comments in files you didn't touch (6_10_sealring.drc:29, 6_9_pad.drc:71, density.drc:29 and :352, 7_3_metalslits.drc:67) still say Activ/pSD/GatPoly/Cont/Metal1 don't exist in this stack. You already fixed the sixth one in 9_1_lbe.drc.
  • LBE.b2 .um to .um2 reads better, though it isn't a behaviour change (30000.um == 30000.um2 == 30000.0). LBE.b1 on line 46 still uses .um for an area if you feel like being consistent.
  • 10_1_tsv_g.drc:93-104 is indented a level deeper than the rest of the deck, and tsvg_bbox = nil on line 103 is dead.

Fixtures and probe used above (no dependency on the repo, save and run)

tsv_review_fixtures.zip
Attaching the fixtures as a zip so you can open them directly instead of running the generator.

@Mauricio-xx

Copy link
Copy Markdown
Collaborator

One more thing on the TSV_G table, separate from the TSV_G.d false positive and much quieter than it. I only found this after building fixtures that carry the enclosing layers, so it was not visible in the first pass.

TSV_G.f and TSV_G.g cannot fail when the enclosing layer is absent

Same conforming ring as before (DeepVia 152/0 annulus, outer 25.0, wall 3.0), varying only what sits underneath:

Metal1 in the layout TSV_G.g
none at all clean
square at half-size 14.0, i.e. exactly 1.5 enclosure clean
square at half-size 13.0, i.e. 0.5 enclosure fires
covers only the left half of the ring fires
4x4 square floating inside the ring hole clean
well clear of the ring, 27.5 um away clean

TSV_G.f mirrors this against PWell:block, I ran the same six cases there too. So the rules are satisfiable and they do discriminate on enclosure width, which is the part that works. What they cannot do is fail when there is no enclosing polygon at all: enclosed() only forms edge pairs where the enclosed shape actually sits inside the enclosing one, so total non-overlap produces no pairs and therefore no output. Partial overlap is caught, total absence is not.

That distinction matters more here than it would elsewhere, because the enclosing Metal1 is not user routing, it is the landing metal the via needs in order to be a via. If the intended flow is that a TSV device draws its own Metal1 and PWell:block at exactly the rule values, then within this deck TSV_G.g is the only check that the landing metal arrived at all, and as written that is the one thing it cannot do.

The deck already carries the idiom for this case. 6_11_mim.drc:75-79 joins a mim_drw.not(metal5_drw) term onto the enclosure result, under the comment "No-landing miss branch: MIM area with no Metal5 above it at all (enclosed() is blind to full non-overlap)", and 6_9_solderbump.drc:109-111 does the same for Padb.c. TSV_G.f and TSV_G.g do not have it. One detail if you take that route: both of those call .polygons on the enclosed() side and these two do not, so joining a miss branch straight on stops the run with "'join': Requires input of the same kind".

The check I would use afterwards: a ring with no Metal1 at all, and a ring whose Metal1 sits well clear of it, both produce TSV_G.g; a square Metal1 at exactly 1.5 stays clean; and the 0.5 row above still fires, so the enclosure check survives rather than being replaced by the miss branch. The one row that has to be decided rather than preserved is the 4x4 square in the hole: a miss branch anchored on the ring alone turns it into a violation, one anchored on the ring plus its opening leaves it clean. Same shape for TSV_G.f against PWell:block at 2.5.

TSV_G.e is correct as written, and should not get the same treatment

Flagging this because the miss branch is tempting to apply table-wide and here it would be wrong. TSV_G.e is a spacing rule, so a vacuous pass against absent Activ, GatPoly and Cont is the arithmetically right answer: "no Activ within 5 um" is true when there is no Activ. I checked that it discriminates properly when the operands do exist, Activ at 0.5 um from the ring fires it and Activ at 6.0 um does not.

Worth checking before anyone writes a testcase for it, though. 10_1_tsv_g.drc:63 builds tsvg_e_targets from activ_drw, activ_filler, gatpoly_drw, gatpoly_filler and cont_drw, so if placed TSVs stake out their keepout with the nofill purposes rather than with these, the rule never sees an operand from a device instance and a testcase has to place the geometry by hand.

The layer declarations, expanding on point 7

More detail on point 7 of the review rather than a new ask. layers_def.drc grows by twenty-five declarations, on the eighteen layer numbers 1, 3, 5, 6, 8, 11, 13, 14, 35, 46, 53, 55, 57, 58, 62, 71, 139, 152 plus 99/32. Only Recog.tsv 99/32 has an entry in the layer authorities; the other twenty-four are absent from intm4tm2_layers.txt, intm4tm2.lyp, intm4tm2.map, intm4tm2.lyt and the LVS layer definitions, and intm4tm2_tests/test_layer_parity.py only compares the lyp against the canonical map, so nothing ties the deck's intake to either.

Eleven of them feed 3_2_forbidden.drc, and that part is coherent, they are declared precisely so they can be rejected. Six have no consumer anywhere in the deck: psd_drw 14/0, pwell_drw 46/0, activ_pin 1/2, gatpoly_pin 5/2, metal1_pin 8/2 and recog_tsv 99/32.

The remaining eight are the live ones: Activ 1/0 and 1/22, GatPoly 5/0 and 5/22, Cont 6/0, Metal1 8/0, PWell:block 46/21 and DeepVia 152/0 itself. Reading them is a decision that this geometry is legitimate in an interposer layout, and that decision is not written down anywhere in the diff. This is a question about the whole table rather than an argument against any particular rule, and I do not think the answer is obvious. It just should not be implicit.

Criterion here is only that it be visible: either the layer authorities get extended alongside the deck, with intm4tm2_layers.txt and the parity test moving together, or the deck states in a comment why it reads layers the rest of the kit does not declare.

Reproducing this takes more than the zip on the previous comment. Its generator draws a conforming Metal1 and PWell:block on every ring it builds, so the no-Metal1 and 0.5-enclosure rows need that context made optional rather than added to, and the other three Metal1 placements are new. The TSV_G.e numbers need Activ fixtures, which that generator never draws at all. Happy to attach a second zip if that is easier than rebuilding it.

@Mauricio-xx

Copy link
Copy Markdown
Collaborator

Heads up that this now shows as blocked, and that is on our side rather than
anything you did. A ruleset went live today that requires the ci-gate check,
and this branch predates the CI, so the workflow is not in the merge ref and the
check cannot appear at all.

Would it be possible to retarget it to dev? That is where work lands now, and
it is enough to make the check run; the base can be changed from Edit next to
the title and nothing else about the PR moves. Happy to do it from our side if
you prefer.

@Mauricio-xx

Mauricio-xx commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Coming back on TSV_G.d with something more useful than the defect report. I had left the fix open on purpose, but after measuring a few formulations there is one that is clearly right, and it also settles the open question from my previous comment, so it is worth naming.

The operand is the problem, not the operator. Every rule in the table except the wall width wants the TSV as a solid disk, not as an annulus. Deriving that disk first, by unioning the DeepVia shapes with their own holes, makes the rest fall into place:

tsv_fill = (deepvia_drw + deepvia_drw.holes).merged

Measured on the same fixtures as before, comparing space(25.0, euclidian) on the annulus against the same call on the disk:

fixture on the annulus on the disk
one conforming TSV 992 edge pairs 0
two TSVs, legal spacing 1984 edge pairs 0
two TSVs, 10 um edge to edge 2412 edge pairs 368

So the false positive is gone and the real violation is still caught, which is the pair of properties a fix has to have. It also makes the space versus isolated question moot: a solid disk has no notch, so once the operand is right the plain spacing check is the correct one. That matters, because swapping the operator instead would silence the false positive while quietly dropping notch detection, and no fixture in the PR would tell you.

TSV_G.b is the exception and should stay on the wall, since the width of the annulus is exactly what it measures.

One thing worth doing while you are in there: TSV_G.a already identifies DeepVia shapes that are not rings, so excluding those from the derivation keeps a single malformed shape from cascading into spacing and enclosure errors that are really just restatements of the same defect.

The same derivation resolves what I left undecided on TSV_G.f and TSV_G.g. Anchoring the no-landing miss branch on the disk rather than on the ring gives the behaviour you want on all six cases, with no anchoring choice left to make:

Metal1 in the layout result
exactly 1.5 enclosure clean
0.5 enclosure fires, from the enclosure term
covers only half the ring fires
4x4 square inside the hole fires, from the miss term
27.5 um clear of the ring fires
none at all fires

Note the enclosure term alone still reports clean for the last three, so the miss branch is doing real work there rather than being belt and braces.

tsv_g_d_en

@yegappan-ihp
yegappan-ihp changed the base branch from main to dev August 18, 2026 11:49
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.

2 participants