Coverage for tests/rcx25/rcx25_test.py: 100%
109 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024 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#
24from __future__ import annotations
26import io
27import json
28import tempfile
30import allure
31import csv_diff
32import os
33import pytest
34from typing import *
36import klayout.db as kdb
37import klayout.lay as klay
39from klayout_pex.kpex_cli import KpexCLI
40from klayout_pex.rcx25.extraction_results import CellExtractionResults
43CSVPath = str
44PNGPath = str
45parent_suite = "kpex/2.5D Extraction Tests"
46tags = ("PEX", "2.5D", "MAGIC")
49def _kpex_pdk_dir() -> str:
50 return os.path.realpath(os.path.join(__file__, '..', '..', '..',
51 'pdk', 'sky130A', 'libs.tech', 'kpex'))
54def _sky130a_testdata_dir() -> str:
55 return os.path.realpath(os.path.join(__file__, '..', '..', '..',
56 'testdata', 'designs', 'sky130A'))
59def _gds(*path_components) -> str:
60 return os.path.join(_sky130a_testdata_dir(), *path_components)
63def _save_layout_preview(gds_path: str,
64 output_png_path: str):
65 kdb.Technology.clear_technologies()
66 default_lyt_path = os.path.abspath(f"{_kpex_pdk_dir()}/sky130A.lyt")
67 tech = kdb.Technology.create_technology('sky130A')
68 tech.load(default_lyt_path)
70 lv = klay.LayoutView()
71 lv.load_layout(gds_path)
72 lv.max_hier()
73 lv.set_config('background-color', '#000000')
74 lv.set_config('bitmap-oversampling', '1')
75 lv.set_config('default-font-size', '4')
76 lv.set_config('default-text-size', '0.1')
77 lv.save_image_with_options(
78 output_png_path,
79 width=4096, height=2160
80 # ,
81 # linewidth=2,
82 # resolution=0.25 # 4x as large fonts
83 )
85def _run_rcx25d_single_cell(*path_components) -> Tuple[CellExtractionResults, CSVPath, PNGPath]:
86 gds_path = _gds(*path_components)
88 preview_png_path = tempfile.mktemp(prefix=f"layout_preview_", suffix=".png")
89 _save_layout_preview(gds_path, preview_png_path)
90 output_dir_path = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'output_sky130A'))
91 cli = KpexCLI()
92 cli.main(['main',
93 '--pdk', 'sky130A',
94 '--gds', gds_path,
95 '--out_dir', output_dir_path,
96 '--2.5D', 'y'])
97 assert cli.rcx25_extraction_results is not None
98 assert len(cli.rcx25_extraction_results.cell_extraction_results) == 1 # assume single cell test
99 results = list(cli.rcx25_extraction_results.cell_extraction_results.values())[0]
100 assert results.cell_name == path_components[-1][:-len('.gds.gz')]
101 return results, cli.rcx25_extracted_csv_path, preview_png_path
104def assert_expected_matches_obtained(*path_components,
105 expected_csv_content: str) -> CellExtractionResults:
106 result, csv, preview_png = _run_rcx25d_single_cell(*path_components)
107 allure.attach.file(csv, name='pex_obtained.csv', attachment_type=allure.attachment_type.CSV)
108 allure.attach.file(preview_png, name='📸 layout_preview.png', attachment_type=allure.attachment_type.PNG)
109 expected_csv = csv_diff.load_csv(io.StringIO(expected_csv_content), key='Device')
110 with open(csv, 'r') as f:
111 obtained_csv = csv_diff.load_csv(f, key='Device')
112 diff = csv_diff.compare(expected_csv, obtained_csv, show_unchanged=False)
113 human_diff = csv_diff.human_text(
114 diff, current=obtained_csv, extras=(('Net1','{Net1}'),('Net2','{Net2}'))
115 )
116 allure.attach(expected_csv_content, name='pex_expected.csv', attachment_type=allure.attachment_type.CSV)
117 allure.attach(json.dumps(diff, sort_keys=True, indent=' ').encode("utf8"),
118 name='pex_diff.json', attachment_type=allure.attachment_type.JSON)
119 allure.attach(human_diff.encode("utf8"), name='‼️ pex_diff.txt', attachment_type=allure.attachment_type.TEXT)
120 # assert diff['added'] == []
121 # assert diff['removed'] == []
122 # assert diff['changed'] == []
123 # assert diff['columns_added'] == []
124 # assert diff['columns_removed'] == []
125 assert human_diff == '', 'Diff detected'
126 return result
128@allure.parent_suite(parent_suite)
129@allure.tag(*tags)
130@pytest.mark.slow
131def test_single_plate_100um_x_100um_li1_over_substrate():
132 # MAGIC GIVES (8.3 revision 485):
133 #_______________________________ NOTE: with halo=8µm __________________________________
134 # C0 PLATE VSUBS 0.38618p
135 assert_expected_matches_obtained(
136 'test_patterns', 'single_plate_100um_x_100um_li1_over_substrate.gds.gz',
137 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
138C1;PLATE;VSUBS;386.18"""
139 )
142@allure.parent_suite(parent_suite)
143@allure.tag(*tags)
144@pytest.mark.slow
145@pytest.mark.wip
146def test_overlap_plates_100um_x_100um_li1_m1():
147 # MAGIC GIVES (8.3 revision 485):
148 #_______________________________ NOTE: with halo=8µm __________________________________
149 # C2 LOWER VSUBS 0.38618p
150 # C0 UPPER LOWER 0.294756p
151 # C1 UPPER VSUBS 0.205833p
152 #_______________________________ NOTE: with halo=50µm __________________________________
153 # C2 LOWER VSUBS 0.38618p
154 # C0 LOWER UPPER 0.294867p
155 # C1 UPPER VSUBS 0.205621p
156 # NOTE: magic with --magic_halo=50 (µm) gives UPPER-VSUBS of 0.205621p
157 # which is due to the handling of https://github.com/martinjankoehler/magic/issues/1
158 assert_expected_matches_obtained(
159 'test_patterns', 'overlap_plates_100um_x_100um_li1_m1.gds.gz',
160 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
161C1;LOWER;VSUBS;386.18
162C2;LOWER;UPPER;294.592
163C3;UPPER;VSUBS;205.52"""
164 )
166@allure.parent_suite(parent_suite)
167@allure.tag(*tags)
168@pytest.mark.slow
169@pytest.mark.wip
170def test_overlap_plates_100um_x_100um_li1_m1_m2_m3():
171 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
172 #_______________________________ NOTE: with halo=8µm __________________________________
173 # C7 li1 VSUBS 0.38618p
174 # C3 li1 met1 0.294756p # DIFFERS a bit !!! TODO
175 # C6 met1 VSUBS 0.205833p # DIFFERS a bit !!! TODO
176 # C0 met1 met2 0.680652p # DIFFERS a bit !!! TODO
177 # C2 li1 met2 99.3128f # DIFFERS a bit !!! TODO
178 # C5 met2 VSUBS 52.151802f
179 # C4 met3 VSUBS 0.136643p
180 # C1 li1 met3 5.59194f
181 #_______________________________ NOTE: with halo=50µm __________________________________
182 # C9 li1 VSUBS 0.38618p
183 # C5 li1 met1 0.294867p # DIFFERS a bit !!! TODO
184 # C8 met1 VSUBS 0.205621p # DIFFERS, but that's a MAGIC issue (see test_overlap_plates_100um_x_100um_li1_m1)
185 # C2 met1 met2 0.680769p
186 # C4 li1 met2 99.518005f # DIFFERS a bit !!! TODO
187 # C7 met2 VSUBS 51.5767f # DIFFERS a bit !!! TODO
188 # C3 li1 met3 6.01281f # DIFFERS !!! TODO
189 # C0 met2 met3 0.0422f # we don't have that?! !!! TODO
190 # C6 met3 VSUBS 0.136103p # DIFFERS a bit !!! TODO
191 # C1 met1 met3 0.012287f # NOTE: we don't have that, due to halo=8µm
193 assert_expected_matches_obtained(
194 'test_patterns', 'overlap_plates_100um_x_100um_li1_m1_m2_m3.gds.gz',
195 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
196C1;VSUBS;li1;386.18
197C2;li1;met1;294.592
198C3;VSUBS;met1;205.52
199C4;met1;met2;680.482
200C5;li1;met2;99.015
201C6;VSUBS;met2;51.302
202C7;VSUBS;met3;135.996
203C8;li1;met3;5.031"""
204 )
207@allure.parent_suite(parent_suite)
208@allure.tag(*tags)
209@pytest.mark.slow
210@pytest.mark.wip
211def test_sidewall_100um_x_100um_distance_200nm_li1():
212 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
213 # _______________________________ NOTE: with halo=8µm __________________________________
214 # C2 C VSUBS 8.231f
215 # C4 A VSUBS 8.231f
216 # C3 B VSUBS 4.54159f
217 # C0 B C 7.5f
218 # C1 A B 7.5f
219 # _______________________________ NOTE: with halo=50µm __________________________________
220 # (same!)
222 assert_expected_matches_obtained(
223 'test_patterns', 'sidewall_100um_x_100um_distance_200nm_li1.gds.gz',
224 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
225C1;C;VSUBS;11.92 # TODO: magic=8.231f
226C2;A;VSUBS;11.92 # TODO: magic=8.231f
227C3;B;VSUBS;11.92 # TODO: magic=4.452f
228C4;B;C;7.5
229C5;A;B;7.5"""
230 )
233@allure.parent_suite(parent_suite)
234@allure.tag(*tags)
235@pytest.mark.slow
236@pytest.mark.wip
237def test_sidewall_net_uturn_l1_redux():
238 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
239 # _______________________________ NOTE: with halo=8µm __________________________________
240 # C2 C0 VSUBS 38.1255f
241 # C1 C1 VSUBS 12.5876f
242 # C0 C0 C1 1.87386f
243 # _______________________________ NOTE: with halo=50µm __________________________________
244 # (same!)
246 assert_expected_matches_obtained(
247 'test_patterns', 'sidewall_net_uturn_l1_redux.gds.gz',
248 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
249C1;C0;VSUBS;40.642
250C2;C1;VSUBS;15.079
251C3;C0;C1;0.019 TODO, MAGIC=1.87386 fF"""
252 )
255@allure.parent_suite(parent_suite)
256@allure.tag(*tags)
257@pytest.mark.slow
258@pytest.mark.wip
259def test_sidewall_cap_vpp_04p4x04p6_l1_redux():
260 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
261 # _______________________________ NOTE: with halo=8µm __________________________________
262 # C2 C0 VSUBS 0.300359f
263 # C1 C1 VSUBS 0.086832f
264 # C0 C0 C1 0.286226f
265 # _______________________________ NOTE: with halo=50µm __________________________________
266 # (same!)
268 assert_expected_matches_obtained(
269 'test_patterns', 'sidewall_cap_vpp_04p4x04p6_l1_redux.gds.gz',
270 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
271C1;C0;VSUBS;0.447 TODO
272C2;C1;VSUBS;0.223 TODO
273C3;C0;C1;0.145 TODO"""
274 )
277@allure.parent_suite(parent_suite)
278@allure.tag(*tags)
279@pytest.mark.slow
280@pytest.mark.wip
281def test_near_body_shield_li1_m1():
282 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
283 #_______________________________ NOTE: with halo=8µm __________________________________
284 # C5 BOTTOM VSUBS 0.405082p
285 # C1 BOTTOM TOPB 0.215823p # DIFFERS marginally <0,1fF
286 # C2 BOTTOM TOPA 0.215823p # DIFFERS marginally <0,1fF
287 # C0 TOPA TOPB 0.502857f
288 # C3 TOPB VSUBS 0.737292f # DIFFERS, but that's a MAGIC issue (see test_overlap_plates_100um_x_100um_li1_m1)
289 # C4 TOPA VSUBS 0.737292f # DIFFERS, but that's a MAGIC issue (see test_overlap_plates_100um_x_100um_li1_m1)
290 #_______________________________ NOTE: with halo=50µm __________________________________
291 # NOTE: with halo=50µm, C3/C4 becomes 0.29976f
292 # see https://github.com/martinjankoehler/magic/issues/2
294 assert_expected_matches_obtained(
295 'test_patterns', 'near_body_shield_li1_m1.gds.gz',
296 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
297C1;BOTTOM;VSUBS;405.082
298C2;BOTTOM;TOPA;215.898
299C3;BOTTOM;TOPB;215.898
300C4;TOPA;TOPB;0.503"""
301 )
304@allure.parent_suite(parent_suite)
305@allure.tag(*tags)
306@pytest.mark.slow
307@pytest.mark.wip
308def test_sideoverlap_simple_plates_li1_m1():
309 # MAGIC GIVES (8.3 revision 485): (sorting changed to match order)
310 # _______________________________ NOTE: with halo=8µm __________________________________
311 # C2 li1 VSUBS 7.931799f
312 # C1 met1 VSUBS 0.248901p
313 # C0 li1 met1 0.143335f
314 # _______________________________ NOTE: with halo=50µm __________________________________
315 # C2 li1 VSUBS 7.931799f
316 # C1 met1 VSUBS 0.248901p
317 # C0 li1 met1 0.156859f
319 assert_expected_matches_obtained(
320 'test_patterns', 'sideoverlap_simple_plates_li1_m1.gds.gz',
321 expected_csv_content="""Device;Net1;Net2;Capacitance [fF]
322C1;VSUBS;li1;7.932
323C2;VSUBS;met1;249.059
324C3;li1;met1;0.125 TODO"""
325 )