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

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 

26 

27import klayout.db as kdb 

28 

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 

34 

35 

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]) 

54 

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 

72 

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') 

77 

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])