Coverage for tests/rcx25/rcx25_netlist_expander_ports_test.py: 100%
44 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-2026 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 unittest
27import klayout.db as kdb
29from klayout_pex.device_models import DeviceModels
30from klayout_pex.rcx25.extraction_results import CellExtractionResults, ExtractionResults
31from klayout_pex.rcx25.netlist_expander import RCX25NetlistExpander
32import klayout_pex_protobuf.kpex.r.r_network_pb2 as r_network_pb2
33import klayout_pex_protobuf.kpex.tech.device_models_pb2 as device_models_pb2
36@allure.parent_suite("Unit Tests")
37@allure.tag("RCX25", "Netlist Expansion")
38class Test(unittest.TestCase):
39 def test_a_net_of_several_labels_has_a_port_per_label(self):
40 # an nmos with its drain on a wire with the labels D and X at its ends (#211 §11)
41 netlist = kdb.Netlist()
42 circuit = kdb.Circuit()
43 circuit.name = 'chip'
44 netlist.add(circuit)
45 nmos = kdb.DeviceClassMOS4Transistor()
46 nmos.name = 'nmos'
47 netlist.add(nmos)
48 nets = {name: circuit.create_net(name) for name in ('D,X', 'G', 'S', 'B')}
49 m1 = circuit.create_device(nmos, 'M1')
50 for terminal, net in (('S', 'S'), ('G', 'G'), ('D', 'D,X'), ('B', 'B')):
51 m1.connect_terminal(terminal, nets[net])
52 for name in ('D,X', 'G', 'S'): # like KLayout's make_top_level_pins, a pin per net
53 circuit.connect_pin(circuit.create_pin(name), nets[name])
55 K = r_network_pb2.RNode.Kind
56 results = CellExtractionResults(cell_name='chip')
57 network = results.r_extraction_result.networks.add(net_name='D,X')
58 for node_id, kind, node_name in ((1, K.KIND_PIN, 'D'),
59 (2, K.KIND_WIRE_JUNCTION, '$1.Metal1'),
60 (3, K.KIND_PIN, 'X'),
61 (4, K.KIND_DEVICE_TERMINAL, 'P0.nSD')):
62 node = network.nodes.add(node_id=node_id, node_kind=kind, node_name=node_name)
63 if kind != K.KIND_PIN:
64 node.net_name = f"D,X.{node_name}"
65 if kind == K.KIND_DEVICE_TERMINAL:
66 node.device_terminal.device_id = m1.id()
67 node.device_terminal.terminal_id = nmos.terminal_id('D')
68 for node_a, node_b, resistance in ((1, 2, 0.488), (2, 3, 0.089), (2, 4, 17.0)):
69 element = network.elements.add(resistance=resistance)
70 element.node_a.node_id = node_a
71 element.node_b.node_id = node_b
73 expanded = RCX25NetlistExpander.expand(netlist, 'chip', ExtractionResults({'chip': results}),
74 blackbox_devices=True,
75 device_models=DeviceModels(device_models_pb2.DeviceModelsInfo()))
76 circuit = expanded.circuit_by_name('chip')
78 # NOTE: the pin of D,X is replaced by the ports D and X, in its place
79 self.assertEqual([('D', 'D'), ('X', 'X'), ('G', 'G'), ('S', 'S')],
80 [(pin.name(), circuit.net_for_pin(pin.id()).name) for pin in circuit.each_pin()])
81 self.assertEqual('D,X', circuit.device_by_name('M1').net_for_terminal('D').name)
82 # NOTE: no port touches nothing (a port the labels had lost, which the parent connected, dangled)
83 self.assertEqual([], [pin.name() for pin in circuit.each_pin()
84 if circuit.net_for_pin(pin.id()).terminal_count() == 0])