Coverage for tests/klayout/netlist_reducer_test.py: 100%

59 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/iic-jku/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# 

24import allure 

25import os 

26import tempfile 

27import unittest 

28 

29import klayout.db as kdb 

30 

31from klayout_pex.log import ( 

32 LogLevel, 

33 set_log_level, 

34) 

35from klayout_pex.klayout.netlist_reducer import NetlistReducer 

36from klayout_pex.klayout.parasitic_device_classes import PARASITIC_CAPACITOR_CLASS_NAME 

37 

38 

39@allure.parent_suite("Unit Tests") 

40@allure.tag("Netlist", "Netlist Reduction") 

41class Test(unittest.TestCase): 

42 @classmethod 

43 def setUpClass(cls): 

44 set_log_level(LogLevel.DEBUG) 

45 

46 @property 

47 def klayout_testdata_dir(self) -> str: 

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

49 'testdata', 'klayout', 'netlists')) 

50 

51 def _test_netlist_reduction(self, netlist_path: str, cell_name: str): 

52 netlist = kdb.Netlist() 

53 reader = kdb.NetlistSpiceReader() 

54 netlist.read(netlist_path, reader) 

55 

56 reducer = NetlistReducer() 

57 reduced_netlist = reducer.reduce(netlist=netlist, top_cell_name=cell_name) 

58 

59 out_path = tempfile.mktemp(prefix=f"{cell_name}_Reduced_Netlist_", suffix=".cir") 

60 spice_writer = kdb.NetlistSpiceWriter() 

61 reduced_netlist.write(out_path, spice_writer) 

62 print(f"Wrote reduced netlist to: {out_path}") 

63 allure.attach.file(out_path, attachment_type=allure.attachment_type.TEXT) 

64 

65 def test_netlist_reduction_1(self): 

66 netlist_path = os.path.join(self.klayout_testdata_dir, 'nmos_diode2_Expanded_Netlist.cir') 

67 self._test_netlist_reduction(netlist_path=netlist_path, cell_name='nmos_diode2') 

68 

69 def test_netlist_reduction_2(self): 

70 netlist_path = os.path.join(self.klayout_testdata_dir, 'cap_vpp_Expanded_Netlist.cir') 

71 self._test_netlist_reduction(netlist_path=netlist_path, cell_name='TOP') 

72 

73 def test_only_parasitic_capacitors_are_reduced(self): 

74 netlist = kdb.Netlist() 

75 circuit = kdb.Circuit() 

76 circuit.name = 'TOP' 

77 netlist.add(circuit) 

78 a = circuit.create_net('a') 

79 b = circuit.create_net('b') 

80 

81 # NOTE: a capacitor of the LVS netlist may have no C parameter, e.g. the IHP MIM (#203) 

82 mim = kdb.DeviceClassCapacitor() 

83 mim.name = 'cap_cmim' 

84 mim.clear_parameters() 

85 for name in ('w', 'l'): 

86 mim.add_parameter(kdb.DeviceParameterDefinition(name, name, 0.0)) 

87 netlist.add(mim) 

88 d = circuit.create_device(mim, 'MIM') 

89 d.connect_terminal('A', a) 

90 d.connect_terminal('B', b) 

91 

92 cap = kdb.DeviceClassCapacitor() 

93 cap.name = PARASITIC_CAPACITOR_CLASS_NAME 

94 netlist.add(cap) 

95 c = circuit.create_device(cap, 'ext_1') 

96 c.connect_terminal('A', a) 

97 c.connect_terminal('B', b) 

98 c.set_parameter('C', 0.01e-15) # below the threshold 

99 

100 reduced_netlist = NetlistReducer().reduce(netlist=netlist, top_cell_name='TOP') 

101 self.assertEqual(['MIM'], [d.name for d in reduced_netlist.circuit_by_name('TOP').each_device()])