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
« 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
29import klayout.db as kdb
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
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)
46 @property
47 def klayout_testdata_dir(self) -> str:
48 return os.path.realpath(os.path.join(__file__, '..', '..', '..',
49 'testdata', 'klayout', 'netlists'))
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)
56 reducer = NetlistReducer()
57 reduced_netlist = reducer.reduce(netlist=netlist, top_cell_name=cell_name)
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)
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')
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')
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')
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)
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
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()])