Coverage for tests/fastercap/fastercap_test.py: 79%

62 statements  

« 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 

25 

26import io 

27import json 

28import tempfile 

29 

30import allure 

31import csv_diff 

32import os 

33import pytest 

34from typing import * 

35 

36import klayout.db as kdb 

37import klayout.lay as klay 

38 

39from klayout_pex.kpex_cli import KpexCLI 

40 

41 

42CSVPath = str 

43PNGPath = str 

44parent_suite = "kpex/FasterCap Extraction Tests" 

45tags = ("PEX", "FasterCap", "Maxwell") 

46 

47 

48def _kpex_pdk_dir() -> str: 

49 return os.path.realpath(os.path.join(__file__, '..', '..', '..', 

50 'pdk', 'sky130A', 'libs.tech', 'kpex')) 

51 

52 

53def _sky130a_testdata_dir() -> str: 

54 return os.path.realpath(os.path.join(__file__, '..', '..', '..', 

55 'testdata', 'designs', 'sky130A')) 

56 

57 

58def _gds(*path_components) -> str: 

59 return os.path.join(_sky130a_testdata_dir(), *path_components) 

60 

61 

62def _save_layout_preview(gds_path: str, 

63 output_png_path: str): 

64 kdb.Technology.clear_technologies() 

65 default_lyt_path = os.path.abspath(f"{_kpex_pdk_dir()}/sky130A.lyt") 

66 tech = kdb.Technology.create_technology('sky130A') 

67 tech.load(default_lyt_path) 

68 

69 lv = klay.LayoutView() 

70 lv.load_layout(gds_path) 

71 lv.max_hier() 

72 lv.set_config('background-color', '#000000') 

73 lv.set_config('bitmap-oversampling', '1') 

74 lv.set_config('default-font-size', '4') 

75 lv.set_config('default-text-size', '0.1') 

76 lv.save_image_with_options( 

77 output_png_path, 

78 width=4096, height=2160 

79 # , 

80 # linewidth=2, 

81 # resolution=0.25 # 4x as large fonts 

82 ) 

83 

84def _extract_single_cell(*path_components) -> Tuple[CSVPath, PNGPath]: 

85 gds_path = _gds(*path_components) 

86 

87 preview_png_path = tempfile.mktemp(prefix=f"layout_preview_", suffix=".png") 

88 _save_layout_preview(gds_path, preview_png_path) 

89 output_dir_path = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'output_sky130A')) 

90 cli = KpexCLI() 

91 cli.main(['main', 

92 '--pdk', 'sky130A', 

93 '--gds', gds_path, 

94 '--out_dir', output_dir_path, 

95 '--fastercap', 'y']) 

96 assert cli.fastercap_extracted_csv_path is not None 

97 return cli.fastercap_extracted_csv_path, preview_png_path 

98 

99 

100def assert_expected_matches_obtained(*path_components, 

101 expected_csv_content: str): 

102 csv, preview_png = _extract_single_cell(*path_components) 

103 allure.attach.file(csv, name='pex_obtained.csv', attachment_type=allure.attachment_type.CSV) 

104 allure.attach.file(preview_png, name='📸 layout_preview.png', attachment_type=allure.attachment_type.PNG) 

105 expected_csv = csv_diff.load_csv(io.StringIO(expected_csv_content), key='Device') 

106 with open(csv, 'r') as f: 

107 obtained_csv = csv_diff.load_csv(f, key='Device') 

108 diff = csv_diff.compare(expected_csv, obtained_csv, show_unchanged=False) 

109 human_diff = csv_diff.human_text( 

110 diff, current=obtained_csv, extras=(('Net1','{Net1}'),('Net2','{Net2}')) 

111 ) 

112 allure.attach(expected_csv_content, name='pex_expected.csv', attachment_type=allure.attachment_type.CSV) 

113 allure.attach(json.dumps(diff, sort_keys=True, indent=' ').encode("utf8"), 

114 name='pex_diff.json', attachment_type=allure.attachment_type.JSON) 

115 allure.attach(human_diff.encode("utf8"), name='‼️ pex_diff.txt', attachment_type=allure.attachment_type.TEXT) 

116 # assert diff['added'] == [] 

117 # assert diff['removed'] == [] 

118 # assert diff['changed'] == [] 

119 # assert diff['columns_added'] == [] 

120 # assert diff['columns_removed'] == [] 

121 assert human_diff == '', 'Diff detected' 

122 

123 

124@allure.parent_suite(parent_suite) 

125@allure.tag(*tags) 

126@pytest.mark.slow 

127def test_single_plate_100um_x_100um_li1_over_substrate(): 

128 #_______________________________ NOTE: with halo=8µm __________________________________ 

129 # C0 PLATE VSUBS 0.38618p 

130 assert_expected_matches_obtained( 

131 'test_patterns', 'single_plate_100um_x_100um_li1_over_substrate.gds.gz', 

132 expected_csv_content="""Device;Net1;Net2;Capacitance [fF] 

133Cext_0_1;VSUBS;PLATE;386.18""" 

134 )