Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -196,22 +196,29 @@ def differential_to_single_ended_converter_netlist(pdk: MappedPDK, half_pload: t
return Netlist(
circuit_name="DIFF_TO_SINGLE",
nodes=['VIN', 'VOUT', 'VSS', 'VSS2'],
source_netlist=""".subckt {circuit_name} {nodes} """ + f'l={half_pload[1]} w={half_pload[0]} mt={4*2} mb={2 * half_pload[2]} ' + """
XTOP1 VOUT VIN VSS VSS {model} l={{l}} w={{w}} m={{mt}}
XTOP2 VSS2 VIN VSS VSS {model} l={{l}} w={{w}} m={{mt}}
XBOT1 VIN VIN VOUT VSS {model} l={{l}} w={{w}} m={{mb}}
XBOT2 VOUT VIN VSS2 VSS {model} l={{l}} w={{w}} m={{mb}}
XDUMMY1 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY2 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY3 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY4 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY5 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY6 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY7 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY8 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY9 VSS VSS VSS VSS {model} l={{l}} w={{w}}
XDUMMY10 VSS VSS VSS VSS {model} l={{l}} w={{w}}
.ends {circuit_name}""",
# No parameters on the top-level .subckt line. KLayout's SPICE reader folds a
# subcircuit's defaults into its NAME -- `l=1.0 w=3.0 mt=8 mb=4` made this
# circuit `DIFF_TO_SINGLE(L=1.0,MT=8,MB=4,W=3.0)` -- so the gf180 deck could
# not match it to the layout's top cell and aborted with "Can't find a
# schematic counterpart for the top cell", which the runner reported as the
# catch-all "LVS inconclusive". Unlike two_transistor_interdigitized the body
# here referenced {l}/{w}/{mt}/{mb}, so those are inlined below.
source_netlist=""".subckt {circuit_name} {nodes}""" + f"""
XTOP1 VOUT VIN VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]} m={4*2}
XTOP2 VSS2 VIN VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]} m={4*2}
XBOT1 VIN VIN VOUT VSS {{model}} l={half_pload[1]} w={half_pload[0]} m={2*half_pload[2]}
XBOT2 VOUT VIN VSS2 VSS {{model}} l={half_pload[1]} w={half_pload[0]} m={2*half_pload[2]}
XDUMMY1 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY2 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY3 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY4 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY5 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY6 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY7 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY8 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY9 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
XDUMMY10 VSS VSS VSS VSS {{model}} l={half_pload[1]} w={half_pload[0]}
.ends {{circuit_name}}""",
instance_format="X{name} {nodes} {circuit_name} l={length} w={width} mt={mult_top} mb={mult_bot}",
parameters={
'model': pdk.models['pfet'],
Expand Down
9 changes: 8 additions & 1 deletion src/glayout/placement/two_transistor_interdigitized.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ def two_tran_interdigitized_netlist(
# X-prefix is sky130's magic+netgen tech expectation; klayout decks
# that classify primitive MOSFETs by SPICE prefix get their netlist
# X→M-rewritten by the LVS runner.
source_netlist = """.subckt {circuit_name} {nodes} """ + f'l={length} w={width} m={1} '+ f"""
# No parameters on the top-level .subckt line. KLayout's SPICE reader folds a
# subcircuit's default parameters into its *name* -- `l=0.28u w=3u m=1` turns the
# circuit into `TWO_TRANSISTOR_INTERDIGITIZED(L=0.28U,M=1,W=3U)` -- so the gf180
# klayout deck can no longer match it to the layout's top cell and aborts with
# "Can't find a schematic counterpart for the top cell". The device lines below
# carry l/w/m explicitly, so these defaults were never used. sky130 is unaffected
# (magic+netgen), which is why this only ever showed up on gf180.
source_netlist = """.subckt {circuit_name} {nodes}""" + f"""
XA VDD1 VG1 VSS1 VB {model} l={length} w={width} m={mtop}
XB VDD2 VG2 VSS2 VB {model} l={length} w={width} m={mtop}"""
if with_dummy:
Expand Down
9 changes: 7 additions & 2 deletions tests/lvs/klayout_gf180.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,12 @@ def run_lvs_klayout_gf180(
FuseTop / met3) or "B" (met4 / FuseTop / met5). The two are mutually
exclusive at process level, so the wrong one extracts no capacitor at all
and every MIM shows up as missing from the layout. Defaults to
``$GF180_MIM_OPTION``, then to "A".
``$GF180_MIM_OPTION``, then to "B" -- which is what glayout actually builds:
both ``mimcap`` and ``mimcap_array`` default to ``option="B"``
(primitives/mimcap.py). This used to default to "A", so every gf180 cell with
a MIM extracted zero capacitors and LVS reported "N schematic device(s)
missing from layout [CAP_MIM_2F0FF]". Set $GF180_MIM_OPTION=A for a layout
genuinely built on the met2/met3 stack.
"""
layout_path = Path(layout)
netlist_path = Path(netlist)
Expand All @@ -310,7 +315,7 @@ def run_lvs_klayout_gf180(
rpt_dir.mkdir(parents=True, exist_ok=True)

pdk_root = pdk_root or os.environ.get("PDK_ROOT", "/foss/pdks")
mim_option = (mim_option or os.environ.get("GF180_MIM_OPTION") or "A").upper()
mim_option = (mim_option or os.environ.get("GF180_MIM_OPTION") or "B").upper()
if mim_option not in ("A", "B"):
raise ValueError(f"mim_option must be 'A' or 'B', got {mim_option!r}")
deck_dir = _resolve_deck_dir(pdk_root)
Expand Down
5 changes: 3 additions & 2 deletions tests/lvs/run_cell_lvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ def main() -> int:
"gf180 MIM stack the deck should extract: A (met2/FuseTop/met3) "
"or B (met4/FuseTop/met5). They are mutually exclusive at process "
"level, so picking the wrong one extracts no capacitor and every "
"MIM reports as missing from the layout. Default: $GF180_MIM_OPTION, "
"else A."
"MIM reports as missing from the layout. glayout builds option B "
"(mimcap/mimcap_array default to option=\"B\"), so that is the "
"default here too. Default: $GF180_MIM_OPTION, else B."
),
)
parser.add_argument(
Expand Down
6 changes: 4 additions & 2 deletions tests/run_ci_locally.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@
uv python install 3.10 >/dev/null 2>&1
PYTHON310="$(uv python find 3.10)"

if [ ! -x /work/.drc-cache/venv/bin/python ]; then
if [ ! -f /work/.drc-cache/venv/.installed ]; then
rm -rf /work/.drc-cache/venv
"$PYTHON310" -m venv /work/.drc-cache/venv
. /work/.drc-cache/venv/bin/activate
uv pip install -e . >/dev/null
uv pip install -e .
touch /work/.drc-cache/venv/.installed
else
# Cache hit: glayout's .pth points to /work which is the same on every
# run, so no refresh is needed (matches the workflows).
Expand Down