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

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# 

24 

25import allure 

26import glob 

27import os 

28import pytest 

29from unittest import mock 

30 

31from klayout_pex.tech_info import TechInfo 

32from rcx25_test_helpers import * 

33 

34CSVPath = str 

35PNGPath = str 

36parent_suite = "kpex/2.5D Extraction Tests [PDK sky130A | mode CC]" 

37tags = ("PEX", "2.5D", "MAGIC") 

38 

39 

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) 

42 

43 

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 ) 

56 

57 

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 ) 

80 

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 

106 

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 ) 

121 

122 

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!) 

134 

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 ) 

142 

143 

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!) 

155 

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 ) 

163 

164 

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!) 

176 

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 ) 

184 

185 

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 

201 

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 ) 

212 

213 

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 ) 

228 

229 

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 

243 

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 ) 

251 

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 

271 

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 ) 

282 

283 

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 # 

309 

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 ) 

333 

334 

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 

345 

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 ) 

353 

354 

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 

371 

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 ) 

388 

389 

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 

406 

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 ) 

420 

421 

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 

455 

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 ) 

470 

471 

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 

498 

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 ) 

517 

518 

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 

534 

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 ) 

542 

543 

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 

553 

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 ) 

561 

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 

571 

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 ) 

579 

580 

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')] == [] 

591 

592 

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 ) 

607 

608 

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 

616 

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'} 

620 

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 ] 

632 

633 

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 

640 

641 

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 ] 

653 

654 

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 ] 

664 

665 

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 ] 

674 

675 

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']