Coverage for tests/rcx25/rcx25_CC_sky130A_test.py: 99%
166 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-02 23:32 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-02 23:32 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2025 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/martinjankoehler/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
25import allure
26import glob
27import os
28import pytest
29from unittest import mock
31from klayout_pex.tech_info import TechInfo
32from rcx25_test_helpers import *
34CSVPath = str
35PNGPath = str
36parent_suite = "kpex/2.5D Extraction Tests [PDK sky130A | mode CC]"
37tags = ("PEX", "2.5D", "MAGIC")
40pex_whiteboxed = RCX25Extraction(pdk=PDKTestConfig(PDKName.SKY130A), pex_mode=PEXMode.CC, blackbox=False)
41pex_blackboxed = RCX25Extraction(pdk=PDKTestConfig(PDKName.SKY130A), pex_mode=PEXMode.CC, blackbox=True)
44@allure.parent_suite(parent_suite)
45@allure.tag(*tags)
46@pytest.mark.slow
47def test_single_plate_100um_x_100um_li1_over_substrate():
48 # MAGIC GIVES (8.3 revision 485):
49 #_______________________________ NOTE: with halo=8µm __________________________________
50 # C0 PLATE VSUBS 0.38618p
51 pex_whiteboxed.assert_expected_matches_obtained(
52 'test_patterns', 'single_plate_100um_x_100um_li1_over_substrate.gds.gz',
53 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
54C1;PLATE;VSUBS;386.179;"""
55 )
58@allure.parent_suite(parent_suite)
59@allure.tag(*tags)
60@pytest.mark.slow
61def test_overlap_plates_100um_x_100um_li1_m1():
62 # MAGIC GIVES (8.3 revision 485):
63 #_______________________________ NOTE: with halo=8µm __________________________________
64 # C2 LOWER VSUBS 0.38618p
65 # C0 UPPER LOWER 0.294756p
66 # C1 UPPER VSUBS 0.205833p
67 #_______________________________ NOTE: with halo=50µm __________________________________
68 # C2 LOWER VSUBS 0.38618p
69 # C0 LOWER UPPER 0.294867p
70 # C1 UPPER VSUBS 0.205621p
71 # NOTE: magic with --magic_halo=50 (µm) gives UPPER-VSUBS of 0.205621p
72 # which is due to the handling of https://github.com/martinjankoehler/magic/issues/1
73 pex_whiteboxed.assert_expected_matches_obtained(
74 'test_patterns', 'overlap_plates_100um_x_100um_li1_m1.gds.gz',
75 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
76C1;LOWER;UPPER;294.867;
77C2;LOWER;VSUBS;386.179;
78C3;UPPER;VSUBS;205.619;"""
79 )
81@allure.parent_suite(parent_suite)
82@allure.tag(*tags)
83@pytest.mark.slow
84def test_overlap_plates_100um_x_100um_li1_m1_m2_m3():
85 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
86 #_______________________________ NOTE: with halo=8µm __________________________________
87 # C7 li1 VSUBS 0.38618p
88 # C6 met1 VSUBS 0.205833p
89 # C5 met2 VSUBS 52.151802f
90 # C4 met3 VSUBS 0.136643p
91 # C3 li1 met1 0.294756p
92 # C0 met1 met2 0.680652p
93 # C2 li1 met2 99.3128f
94 # C1 li1 met3 5.59194f
95 #_______________________________ NOTE: with halo=50µm __________________________________
96 # C9 li1 VSUBS 0.38618p
97 # C8 met1 VSUBS 0.205621p
98 # C7 met2 VSUBS 51.5767f
99 # C6 met3 VSUBS 0.136103p
100 # C5 li1 met1 0.294867p
101 # C4 li1 met2 99.518005f
102 # C2 met1 met2 0.680769p
103 # C3 li1 met3 6.01281f
104 # C1 met1 met3 0.012287f
105 # C0 met2 met3 0.0422f
107 pex_whiteboxed.assert_expected_matches_obtained(
108 'test_patterns', 'overlap_plates_100um_x_100um_li1_m1_m2_m3.gds.gz',
109 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
110C1;li1;met1;294.867;
111C2;li1;met2;99.518;
112C3;li1;met3;6.013;
113C4;met1;met2;680.769;
114C5;met1;met3;0.016;
115C6;met2;met3;0.056;
116C7;VSUBS;li1;386.179;
117C8;VSUBS;met1;205.619;
118C9;VSUBS;met2;51.574;
119C10;VSUBS;met3;136.063;"""
120 )
123@allure.parent_suite(parent_suite)
124@allure.tag(*tags)
125@pytest.mark.slow
126def test_sidewall_100um_x_100um_distance_200nm_li1():
127 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
128 # _______________________________ NOTE: with halo=8µm __________________________________
129 # C0 A B 7.5f
130 # C1 B VSUBS 8.231f
131 # C2 A VSUBS 8.231f
132 # _______________________________ NOTE: with halo=50µm __________________________________
133 # (same!)
135 pex_whiteboxed.assert_expected_matches_obtained(
136 'test_patterns', 'sidewall_100um_x_100um_distance_200nm_li1.gds.gz',
137 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
138C1;A;B;3.75;
139C2;A;VSUBS;8.231;
140C3;B;VSUBS;8.231;"""
141 )
144@allure.parent_suite(parent_suite)
145@allure.tag(*tags)
146@pytest.mark.slow
147def test_sidewall_net_uturn_l1_redux():
148 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
149 # _______________________________ NOTE: with halo=8µm __________________________________
150 # C1 C1 VSUBS 12.5876f
151 # C2 C0 VSUBS 38.1255f
152 # C0 C0 C1 1.87386f
153 # _______________________________ NOTE: with halo=50µm __________________________________
154 # (same!)
156 pex_whiteboxed.assert_expected_matches_obtained(
157 'test_patterns', 'sidewall_net_uturn_l1_redux.gds.gz',
158 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
159C1;C0;C1;0.937;
160C2;C0;VSUBS;38.125;
161C3;C1;VSUBS;12.588;"""
162 )
165@allure.parent_suite(parent_suite)
166@allure.tag(*tags)
167@pytest.mark.slow
168def test_sidewall_cap_vpp_04p4x04p6_l1_redux():
169 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
170 # _______________________________ NOTE: with halo=8µm __________________________________
171 # C1 C1 VSUBS 0.086832f
172 # C2 C0 VSUBS 0.300359f
173 # C0 C0 C1 0.286226f
174 # _______________________________ NOTE: with halo=50µm __________________________________
175 # (same!)
177 pex_whiteboxed.assert_expected_matches_obtained(
178 'test_patterns', 'sidewall_cap_vpp_04p4x04p6_l1_redux.gds.gz',
179 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
180C1;C0;C1;0.143;
181C2;C0;VSUBS;0.3;
182C3;C1;VSUBS;0.087;"""
183 )
186@allure.parent_suite(parent_suite)
187@allure.tag(*tags)
188@pytest.mark.slow
189def test_near_body_shield_li1_m1():
190 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
191 #_______________________________ NOTE: with halo=8µm __________________________________
192 # C5 BOTTOM VSUBS 0.405082p
193 # C1 BOTTOM TOPB 0.215823p # DIFFERS marginally <0,1fF
194 # C2 BOTTOM TOPA 0.215823p # DIFFERS marginally <0,1fF
195 # C0 TOPA TOPB 0.502857f
196 # C3 TOPB VSUBS 0.737292f # DIFFERS, but that's a MAGIC issue (see test_overlap_plates_100um_x_100um_li1_m1)
197 # C4 TOPA VSUBS 0.737292f # DIFFERS, but that's a MAGIC issue (see test_overlap_plates_100um_x_100um_li1_m1)
198 #_______________________________ NOTE: with halo=50µm __________________________________
199 # NOTE: with halo=50µm, C3/C4 becomes 0.29976f
200 # see https://github.com/martinjankoehler/magic/issues/2
202 pex_whiteboxed.assert_expected_matches_obtained(
203 'test_patterns', 'near_body_shield_li1_m1.gds.gz',
204 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
205C1;BOTTOM;TOPA;215.972;
206C2;BOTTOM;TOPB;215.972;
207C3;BOTTOM;VSUBS;405.081;
208C4;TOPA;TOPB;0.251;
209C5;TOPA;VSUBS;0.299;
210C6;TOPB;VSUBS;0.299;"""
211 )
214@allure.parent_suite(parent_suite)
215@allure.tag(*tags)
216@pytest.mark.slow
217def test_lateral_fringe_shield_by_same_polygon_li1():
218 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
219 #_______________________________ NOTE: with halo=8µm __________________________________
220 # C0 C0 VSUBS 6.41431f $ **FLOATING
221 #_______________________________ NOTE: with halo=50µm __________________________________
222 # C0 C0 VSUBS 6.41431f $ **FLOATING
223 pex_whiteboxed.assert_expected_matches_obtained(
224 'test_patterns', 'lateral_fringe_shield_by_same_polygon_li1.gds.gz',
225 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
226C1;C0;VSUBS;6.414;"""
227 )
230@allure.parent_suite(parent_suite)
231@allure.tag(*tags)
232@pytest.mark.slow
233def test_sideoverlap_simple_plates_li1_m1():
234 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
235 # _______________________________ NOTE: with halo=8µm __________________________________
236 # C2 li1 VSUBS 7.931799f
237 # C1 met1 VSUBS 0.248901p
238 # C0 li1 met1 0.143335f
239 # _______________________________ NOTE: with halo=50µm __________________________________
240 # C2 li1 VSUBS 7.931799f
241 # C1 met1 VSUBS 0.248901p
242 # C0 li1 met1 0.156859f
244 pex_whiteboxed.assert_expected_matches_obtained(
245 'test_patterns', 'sideoverlap_simple_plates_li1_m1.gds.gz',
246 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
247C1;li1;met1;0.157;
248C2;VSUBS;li1;7.931;
249C3;VSUBS;met1;248.899;"""
250 )
252@allure.parent_suite(parent_suite)
253@allure.tag(*tags)
254@pytest.mark.slow
255def test_sideoverlap_shielding_simple_plates_li1_m1_m2():
256 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
257 # _______________________________ NOTE: with halo=8µm __________________________________
258 # C5 li1 VSUBS 11.7936f
259 # C4 met1 VSUBS 57.990803f
260 # C2 li1 met1 15.661301f
261 # C0 met1 met2 0.257488p
262 # C3 met2 VSUBS 5.29197f
263 # C1 li1 met2 0.151641f
264 # _______________________________ NOTE: with halo=50µm __________________________________
265 # C5 li1 VSUBS 11.7936f
266 # C4 met1 VSUBS 57.990803f
267 # C2 li1 met1 15.709599f
268 # C0 met1 met2 0.257488p
269 # C3 met2 VSUBS 5.29197f
270 # C1 li1 met2 0.151641f
272 pex_whiteboxed.assert_expected_matches_obtained(
273 'test_patterns', 'sideoverlap_shielding_simple_plates_li1_m1_m2.gds.gz',
274 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
275C1;li1;met1;15.71;
276C2;li1;met2;0.152;
277C3;met1;met2;257.488;
278C4;VSUBS;li1;11.793;
279C5;VSUBS;met1;57.99;
280C6;VSUBS;met2;5.291;"""
281 )
284@allure.parent_suite(parent_suite)
285@allure.tag(*tags)
286@pytest.mark.slow
287def test_sideoverlap_plates_li1_m1():
288 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
289 # _______________________________ NOTE: with halo=50µm __________________________________
290 # C15 LOWER_NoHaloOverlap_InsideTop VSUBS 51.9938f
291 # C12 LOWER_OutsideHalo VSUBS 73.274605f
292 # C17 LOWER_PartialSideHaloOverlap_Separated VSUBS 90.6184f
293 # C13 LOWER_PartialSideHaloOverlap_BothSides_separated VSUBS 7.93086f
294 # C14 LOWER_PartialSideHaloOverlap_Touching VSUBS 13.637f
295 # C16 LOWER_FullHaloOverlap VSUBS 0.177602p
296 # C11 UPPER VSUBS 0.214853p
297 # C8 LOWER_NoHaloOverlap_InsideTop UPPER 0.146991p
298 # C7 LOWER_PartialSideHaloOverlap_Touching UPPER 32.1587f
299 # C10 LOWER_FullHaloOverlap UPPER 0.262817p
300 # C3 LOWER_FullHaloOverlap LOWER_NoHaloOverlap_InsideTop 0.12574f
301 # C2 LOWER_NoHaloOverlap_InsideTop LOWER_PartialSideHaloOverlap_Touching 0.063307f
302 # C9 LOWER_NoHaloOverlap_InsideTop LOWER_OutsideHalo 0.06287f
303 # C1 LOWER_FullHaloOverlap LOWER_OutsideHalo 0.100592f
304 # C4 LOWER_PartialSideHaloOverlap_Separated LOWER_FullHaloOverlap 0.248054f
305 # C5 LOWER_OutsideHalo UPPER 0.076223f
306 # C6 LOWER_PartialSideHaloOverlap_BothSides_separated UPPER 0.261432f
307 # C0 LOWER_PartialSideHaloOverlap_Separated UPPER 0.148834f
308 #
310 pex_whiteboxed.assert_expected_matches_obtained(
311 'test_patterns', 'sideoverlap_plates_li1_m1.gds.gz',
312 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
313C1;LOWER_FullHaloOverlap;LOWER_NoHaloOverlap_InsideTop;0.063;
314C2;LOWER_FullHaloOverlap;LOWER_OutsideHalo;0.05;
315C3;LOWER_FullHaloOverlap;LOWER_PartialSideHaloOverlap_BothSides_separated;0.0;
316C4;LOWER_FullHaloOverlap;LOWER_PartialSideHaloOverlap_Separated;0.124;
317C5;LOWER_FullHaloOverlap;UPPER;262.817;
318C6;LOWER_FullHaloOverlap;VSUBS;177.601;
319C7;LOWER_NoHaloOverlap_InsideTop;LOWER_OutsideHalo;0.031;
320C8;LOWER_NoHaloOverlap_InsideTop;LOWER_PartialSideHaloOverlap_Touching;0.032;
321C9;LOWER_NoHaloOverlap_InsideTop;UPPER;146.991;
322C10;LOWER_NoHaloOverlap_InsideTop;VSUBS;51.994;
323C11;LOWER_OutsideHalo;UPPER;0.076;
324C12;LOWER_OutsideHalo;VSUBS;73.274;
325C13;LOWER_PartialSideHaloOverlap_BothSides_separated;UPPER;0.261;
326C14;LOWER_PartialSideHaloOverlap_BothSides_separated;VSUBS;7.931;
327C15;LOWER_PartialSideHaloOverlap_Separated;UPPER;0.149;
328C16;LOWER_PartialSideHaloOverlap_Separated;VSUBS;90.618;
329C17;LOWER_PartialSideHaloOverlap_Touching;UPPER;32.159;
330C18;LOWER_PartialSideHaloOverlap_Touching;VSUBS;13.637;
331C19;UPPER;VSUBS;214.85;"""
332 )
335@allure.parent_suite(parent_suite)
336@allure.tag(*tags)
337@pytest.mark.slow
338def test_sideoverlap_fingered_li1_m1_patternA():
339 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
340 # _______________________________ NOTE: with halo=50µm __________________________________
341 #
342 # C2 LOWER VSUBS 5.89976f
343 # C1 UPPER VSUBS 72.328f
344 # C0 LOWER UPPER 0.357768f
346 pex_whiteboxed.assert_expected_matches_obtained(
347 'test_patterns', 'sideoverlap_fingered_li1_m1_patternA.gds.gz',
348 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
349C1;LOWER;UPPER;0.358;
350C2;LOWER;VSUBS;5.9;
351C3;UPPER;VSUBS;72.327;"""
352 )
355@allure.parent_suite(parent_suite)
356@allure.tag(*tags)
357@pytest.mark.slow
358def test_sideoverlap_fingered_li1_m1():
359 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
360 # _______________________________ NOTE: with halo=50µm __________________________________
361 #
362 # C6 LOWER_PartialSideHaloOverlap_Fingered2 VSUBS 8.15974f
363 # C8 LOWER_PartialSideHaloOverlap_Fingered3 VSUBS 8.16395f
364 # C7 LOWER_PartialSideHaloOverlap_Fingered1 VSUBS 5.8844f
365 # C5 LOWER_PartialSideHaloOverlap_Fingered4 VSUBS 5.88862f
366 # C4 UPPER VSUBS 0.215283p
367 # C0 LOWER_PartialSideHaloOverlap_Fingered3 UPPER 0.158769f
368 # C2 LOWER_PartialSideHaloOverlap_Fingered2 UPPER 2.46581f
369 # C1 LOWER_PartialSideHaloOverlap_Fingered4 UPPER 0.35839f
370 # C3 LOWER_PartialSideHaloOverlap_Fingered1 UPPER 0.244356f
372 pex_whiteboxed.assert_expected_matches_obtained(
373 'test_patterns', 'sideoverlap_fingered_li1_m1.gds.gz',
374 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
375C1;LOWER_PartialSideHaloOverlap_Fingered1;LOWER_PartialSideHaloOverlap_Fingered2;0.002;
376C2;LOWER_PartialSideHaloOverlap_Fingered1;LOWER_PartialSideHaloOverlap_Fingered4;0.008;
377C3;LOWER_PartialSideHaloOverlap_Fingered1;UPPER;0.244;
378C4;LOWER_PartialSideHaloOverlap_Fingered1;VSUBS;5.884;
379C5;LOWER_PartialSideHaloOverlap_Fingered2;LOWER_PartialSideHaloOverlap_Fingered3;0.001;
380C6;LOWER_PartialSideHaloOverlap_Fingered2;UPPER;2.466;
381C7;LOWER_PartialSideHaloOverlap_Fingered2;VSUBS;8.16;
382C8;LOWER_PartialSideHaloOverlap_Fingered3;UPPER;0.159;
383C9;LOWER_PartialSideHaloOverlap_Fingered3;VSUBS;8.164;
384C10;LOWER_PartialSideHaloOverlap_Fingered4;UPPER;0.358;
385C11;LOWER_PartialSideHaloOverlap_Fingered4;VSUBS;5.889;
386C12;UPPER;VSUBS;215.281;"""
387 )
390@allure.parent_suite(parent_suite)
391@allure.tag(*tags)
392@pytest.mark.slow
393def test_sideoverlap_complex_li1_m1():
394 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
395 # _______________________________ NOTE: with halo=50µm __________________________________
396 #
397 # C6 Complex_Shape_L VSUBS 3.19991f
398 # C8 Complex_Shape_T VSUBS 3.19991f
399 # C7 Complex_Shape_R VSUBS 3.19991f
400 # C5 Complex_Shape_B VSUBS 3.19991f
401 # C4 UPPER VSUBS 13.0192f
402 # C0 Complex_Shape_B UPPER 1.34751f
403 # C3 Complex_Shape_T UPPER 0.064969f
404 # C2 Complex_Shape_R UPPER 0.089357f
405 # C1 Complex_Shape_L UPPER 0.24889f
407 pex_whiteboxed.assert_expected_matches_obtained(
408 'test_patterns', 'sideoverlap_complex_li1_m1.gds.gz',
409 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
410C1;Complex_Shape_B;UPPER;1.348;
411C2;Complex_Shape_B;VSUBS;3.2;
412C3;Complex_Shape_L;UPPER;0.249;
413C4;Complex_Shape_L;VSUBS;3.2;
414C5;Complex_Shape_R;UPPER;0.089;
415C6;Complex_Shape_R;VSUBS;3.2;
416C7;Complex_Shape_T;UPPER;0.065;
417C8;Complex_Shape_T;VSUBS;3.2;
418C9;UPPER;VSUBS;13.019;"""
419 )
422@allure.parent_suite(parent_suite)
423@allure.tag(*tags)
424@pytest.mark.slow
425def test_met1_over_drain_li1():
426 # The met1 plate W (4 µm x 2 µm) is over the drain diffusion of the nfet (W = 2 µm, L = 0.15 µm),
427 # 33.6 aF/µm² * 8 µm² = 268.8 aF to D, plus the fringe to the drain beyond the plate.
428 # The diffusion used to be no part of the extraction, so W had 25.78 aF/µm² to the substrate instead
429 # (D-W 0.002 fF, VSUBS-W 0.69 fF).
430 # G is higher than with MAGIC, as the gate poly over the channel is poly here, rather than the transistor
431 #
432 # MAGIC GIVES (8.3 revision 681): (sorting changed to match order)
433 # _______________________________ NOTE: with halo=8µm __________________________________
434 # C2 G D 0.00247f
435 # C1 S D 0.00581f
436 # C7 D VSUBS 0.03027f
437 # C4 D W 0.37655f
438 # C3 G S 0.00323f
439 # C9 G VSUBS 0.18508f
440 # C0 G W 0.0014f
441 # C8 S VSUBS 0.03027f
442 # C5 S W 0.01027f
443 # C6 W VSUBS 0.38766f
444 # _______________________________ NOTE: with halo=50µm __________________________________
445 # C4 G D 0.00247f
446 # C3 S D 0.00645f
447 # C7 D VSUBS 0.02884f
448 # C0 D W 0.37655f
449 # C5 G S 0.00323f
450 # C9 G VSUBS 0.18508f
451 # C2 G W 0.0014f
452 # C8 S VSUBS 0.02884f
453 # C1 S W 0.01027f
454 # C6 W VSUBS 0.38766f
456 pex_whiteboxed.assert_expected_matches_obtained(
457 'test_patterns', 'nfet_li1_met1_over_drain.gds.gz',
458 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
459C1;D;G;0.002;
460C2;D;S;0.006;
461C3;D;VSUBS;0.029;
462C4;D;W;0.377;
463C5;G;S;0.004;
464C6;G;VSUBS;0.235;
465C7;G;W;0.007;
466C8;S;VSUBS;0.029;
467C9;S;W;0.01;
468C10;VSUBS;W;0.388;"""
469 )
472@allure.parent_suite(parent_suite)
473@allure.tag(*tags)
474@pytest.mark.slow
475def test_inverter_over_nwell():
476 # The capacitances of the shapes over the nwell go to its net VPB, the others to the substrate.
477 # They all used to go to the substrate (e.g. VPWR-VSUBS 0.273 fF, which is 0.211 fF and VPB-VPWR 0.062 fF now).
478 # A is higher than with MAGIC, as the gate poly over the channel is poly here, rather than the transistor,
479 # and VPB-VNB (VSUBS) is the capacitance of the nwell itself, which MAGIC has, but the tech info doesn't
480 #
481 # MAGIC GIVES (8.3 revision 681): (sorting changed to match order)
482 # _______________________________ NOTE: with halo=8µm and halo=50µm _____________________
483 # C2 A VGND 0.03709f
484 # C8 VPB A 0.04506f
485 # C6 A VPWR 0.03629f
486 # C13 A VNB 0.13301f
487 # C4 A Y 0.03773f
488 # C3 VPB VGND 0.01319f
489 # C0 VPWR VGND 0.01841f
490 # C10 VGND VNB 0.24421f
491 # C9 Y VGND 0.05975f
492 # C7 VPB VPWR 0.06649f
493 # C5 VPB Y 0.01774f
494 # C12 VPWR VNB 0.20582f
495 # C1 VPWR Y 0.07413f
496 # C11 Y VNB 0.0961f
497 # C14 VPB VNB 0.33898f
499 pex_whiteboxed.assert_expected_matches_obtained(
500 'sky130_fd_sc_hd__inv_1', 'sky130_fd_sc_hd__inv_1.gds.gz',
501 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
502C1;A;VGND;0.04;
503C2;A;VPB;0.091;
504C3;A;VPWR;0.041;
505C4;A;VSUBS;0.222;
506C5;A;Y;0.051;
507C6;VGND;VPB;0.012;
508C7;VGND;VPWR;0.018;
509C8;VGND;VSUBS;0.248;
510C9;VGND;Y;0.06;
511C10;VPB;VPWR;0.062;
512C11;VPB;Y;0.018;
513C12;VPWR;VSUBS;0.211;
514C13;VPWR;Y;0.074;
515C14;VSUBS;Y;0.096;"""
516 )
519@allure.parent_suite(parent_suite)
520@allure.tag(*tags)
521@pytest.mark.slow
522def test_met2_over_met1_over_nwell_of_its_net():
523 # The nwell is VDD (by an ntap), and so is the met1 plate (8 µm x 8 µm) over it, which has no capacitance
524 # to it, and shields it from the met2 plate X (4 µm x 4 µm) above.
525 # The fringe of X beyond the met1 plate ends at the nwell (VDD), and beyond that, at the substrate.
526 # The nwell used to be the substrate (VDD-VSUBS 3.003 fF, VDD-X 3.088 fF, VSUBS-X 0.369 fF).
527 # MAGIC has the capacitance of the nwell itself too (100 µm² * 120 aF/µm² = 12 fF), which the tech info hasn't
528 #
529 # MAGIC GIVES (8.3 revision 681): (sorting changed to match order)
530 # _______________________________ NOTE: with halo=50µm __________________________________
531 # C2 VDD VSUBS 12.9206f
532 # C0 VDD X 3.16499f
533 # C1 X VSUBS 0.29271f
535 pex_whiteboxed.assert_expected_matches_obtained(
536 'test_patterns', 'nwell_met1_met2_plates.gds.gz',
537 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
538C1;VDD;VSUBS;0.923;
539C2;VDD;X;3.165;
540C3;VSUBS;X;0.293;"""
541 )
544@allure.parent_suite(parent_suite)
545@allure.tag(*tags)
546@pytest.mark.slow
547def test_mom_cap__whiteboxed():
548 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
549 # _______________________________ NOTE: with halo=50µm __________________________________
550 # C0 C0 C1 13.4653f
551 # C1 C1 SUB 0.77621f
552 # C2 C0 SUB 2.83686f
554 pex_whiteboxed.assert_expected_matches_obtained(
555 'cap_vpp_04p4x04p6_l1m1m2_noshield', 'cap_vpp_04p4x04p6_l1m1m2_noshield.gds.gz',
556 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
557C1;C0;C1;7.739;
558C2;C0;VSUBS;2.827;
559C3;C1;VSUBS;0.765;"""
560 )
562@allure.parent_suite(parent_suite)
563@allure.tag(*tags)
564@pytest.mark.slow
565def test_mom_cap__blackboxed():
566 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
567 # _______________________________ NOTE: with halo=50µm __________________________________
568 # C0 C0 C1 13.4653f
569 # C1 C1 SUB 0.77621f
570 # C2 C0 SUB 2.83686f
572 pex_blackboxed.assert_expected_matches_obtained(
573 'cap_vpp_04p4x04p6_l1m1m2_noshield', 'cap_vpp_04p4x04p6_l1m1m2_noshield.gds.gz',
574 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
575C1;C0;C1;0.141;
576C2;C0;VSUBS;3.562;
577C3;C1;VSUBS;0.117;"""
578 )
581@allure.parent_suite(parent_suite)
582@allure.tag(*tags)
583@pytest.mark.slow
584def test_mom_cap_with_4_terminals__whiteboxed():
585 # The LVS script reads VPP caps with 4 terminals as MOS4, so white-box mode kept the device,
586 # whose capacitance is extracted from its fingers: counted twice
587 _, csv_path, _ = pex_whiteboxed.run_rcx25d_single_cell('cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5',
588 'cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5.gds.gz')
589 with open(csv_path[:-len('.csv')] + '.spice') as f:
590 assert [line for line in f if line.startswith('X')] == []
593@allure.parent_suite(parent_suite)
594@allure.tag(*tags)
595@pytest.mark.slow
596def test_mim_cap__whiteboxed():
597 # The device model gives camimc 2.0 fF/µm² * 18.9 µm * 5.1 µm = 192.78 fF (capm over met3)
598 # plus cpmimc 0.19 fF/µm * 48 µm = 9.12 fF, the fringe to the bottom plate around the top plate
599 # (8.36 fF from capm to met3 here), the rest is the interconnect
600 pex_whiteboxed.assert_expected_matches_obtained(
601 'cap_mim_m3_w18p9_l5p1', 'cap_mim_m3_w18p9_l5p1.gds.gz',
602 expected_csv_content="""Device;Net1;Net2;Capacitance [fF];Resistance [Ω]
603C1;mimcap_bot;mimcap_top;204.88;
604C2;VSUBS;mimcap_bot;4.353;
605C3;VSUBS;mimcap_top;2.942;"""
606 )
609@allure.parent_suite(parent_suite)
610@allure.tag(*tags)
611@pytest.mark.slow
612def test_mim_cap__whiteboxed_without_overlap_caps_is_an_error():
613 # Without the overlap capacitances of the MIM plate capm, they would be missing,
614 # which crashed the sidewall extraction with a KeyError
615 overlap_cap_by_layer_names = TechInfo.overlap_cap_by_layer_names.func
617 def without_capm(tech_info: TechInfo):
618 return {top: {bottom: oc for bottom, oc in ocs.items() if bottom != 'capm'}
619 for top, ocs in overlap_cap_by_layer_names(tech_info).items() if top != 'capm'}
621 with mock.patch.object(TechInfo, 'overlap_cap_by_layer_names', property(without_capm)), \
622 mock.patch('klayout_pex.kpex_cli.error') as error_mock, pytest.raises(SystemExit):
623 pex_whiteboxed.run_rcx25d_single_cell('cap_mim_m3_w18p9_l5p1', 'cap_mim_m3_w18p9_l5p1.gds.gz')
624 assert [c.args[0] for c in error_mock.call_args_list if 'overlap' in c.args[0]] == [
625 "The tech info has no overlap capacitance for these layer pairs of the layout, "
626 "so their capacitances would be missing (e.g. the plates of a MIM cap, "
627 "which --blackbox leaves to the device model):\n"
628 " - capm over VSUBS\n"
629 " - capm over met3\n"
630 " - met4 over capm"
631 ]
634@allure.parent_suite(parent_suite)
635@allure.tag(*tags)
636@pytest.mark.slow
637def test_mim_cap__blackboxed():
638 results, _, _ = pex_blackboxed.run_rcx25d_single_cell('cap_mim_m3_w18p9_l5p1', 'cap_mim_m3_w18p9_l5p1.gds.gz')
639 assert results.summarize().capacitances
642@allure.parent_suite(parent_suite)
643@allure.tag(*tags)
644@pytest.mark.slow
645def test_varactors__blackboxed():
646 # The varactors of the PDK's device generator (magic), W along the poly, L across it:
647 # the lvt one with W < L, the hvt one with W > L. Their models depend on w and l separately,
648 # so LVS extracts them like MOS gates, rather than by area and perimeter, which can't tell w from l
649 assert pex_blackboxed.written_device_lines('test_patterns', 'cap_var_lvt_w1_l2_hvt_w2_l0p5.gds.gz') == [
650 'X$1 LVT_C0 LVT_C1 LVT_B sky130_fd_pr__cap_var_lvt w=1 l=2',
651 'X$2 HVT_C0 HVT_C1 HVT_B sky130_fd_pr__cap_var_hvt w=2 l=0.5',
652 ]
655@allure.parent_suite(parent_suite)
656@allure.tag(*tags)
657@pytest.mark.slow
658def test_hv_diffusion_resistors__blackboxed():
659 # The HV diffusion resistors of the PDK's device generator (magic), their models are named ..._nd__hv, ..._pd__hv
660 assert pex_blackboxed.written_device_lines('test_patterns', 'res_generic_nd_hv_pd_hv_w1_l4.gds.gz') == [
661 'X$1 ND_R2 ND_R1 sky130_gnd sky130_fd_pr__res_generic_nd__hv w=1 l=4',
662 'X$2 PD_R2 PD_R1 PD_B sky130_fd_pr__res_generic_pd__hv w=1 l=4',
663 ]
666@allure.parent_suite(parent_suite)
667@allure.tag(*tags)
668@pytest.mark.slow
669def test_sonos_fet__blackboxed():
670 # The SONOS FET of the PDK's device generator (magic), with the core marker areaid.ce, which LVS needs for it
671 assert pex_blackboxed.written_device_lines('test_patterns', 'sonosfet_star_w0p45_l0p22.gds.gz') == [
672 'X$1 S G D sky130_gnd sky130_fd_bs_flash__special_sonosfet_star l=0.22 w=0.45 as=0.1305 ad=0.1305 ps=1.48 pd=1.48',
673 ]
676@allure.parent_suite(parent_suite)
677@allure.tag(*tags)
678@pytest.mark.slow
679def test_label_of_substrate_under_inductor_names_the_substrate():
680 # A label (SUB) names the substrate under the inductor marker, like the ones of the PDK's VPP caps,
681 # so the capacitances to the substrate (of the met1 plate over the marker) go to SUB,
682 # like the bulk of the nfet next to it. It used to name a net of its own, that connected to nothing
683 pex_whiteboxed.run_rcx25d_single_cell('inductor_substrate_pin', 'inductor_substrate_pin.gds.gz')
684 output_dir_path = os.path.realpath(os.path.join(__file__, '..', '..', '..', f"output_{pex_whiteboxed.pdk.name}"))
685 netlist_path, = glob.glob(os.path.join(output_dir_path, 'inductor_substrate_pin__*', '*_k25d_pex_netlist.spice'))
686 with open(netlist_path) as f:
687 lines = f.read().replace('\n+', ' ').splitlines()
688 subckt_line, = [l for l in lines if l.startswith('.SUBCKT ')]
689 nfet_line, = [l for l in lines if l.startswith('X$1 ')]
690 plate_cap_line, = [l for l in lines if l.startswith('C') and 'PLATE' in l.split()[1:3]]
691 assert subckt_line.split()[2:] == ['D', 'G', 'S', 'SUB']
692 assert nfet_line.split()[4] == 'SUB' # B
693 assert sorted(plate_cap_line.split()[1:3]) == ['PLATE', 'SUB']