Coverage for klayout_pex/rcx25/netlist_expander.py: 13%

67 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-08 18:54 +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/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 re 

27from typing import * 

28 

29import klayout.db as kdb 

30 

31from ..log import ( 

32 info, 

33 warning, 

34) 

35from .extraction_results import ExtractionResults 

36 

37 

38class RCX25NetlistExpander: 

39 @staticmethod 

40 def expand(extracted_netlist: kdb.Netlist, 

41 top_cell_name: str, 

42 extraction_results: ExtractionResults, 

43 blackbox_devices: bool) -> kdb.Netlist: 

44 expanded_netlist: kdb.Netlist = extracted_netlist.dup() 

45 top_circuit: kdb.Circuit = expanded_netlist.circuit_by_name(top_cell_name) 

46 

47 if not blackbox_devices: 

48 # TODO: we'll need additional information about the available devices 

49 # because we only want to replace resistor / capacitor devices 

50 # and for example not transitors 

51 

52 for d in top_circuit.each_device(): 

53 name = d.name or d.expanded_name() 

54 match d.device_class().__class__: 

55 case kdb.DeviceClassResistor | kdb.DeviceClassResistorWithBulk: 

56 pass 

57 

58 case kdb.DeviceClassCapacitor | kdb.DeviceClassCapacitorWithBulk: 

59 info(f"Removing whiteboxed device {name}") 

60 top_circuit.remove_device(d) 

61 

62 case kdb.DeviceClassInductor: 

63 pass 

64 

65 case kdb.DeviceClassBJT3Transistor | kdb.DeviceClassBJT4Transistor | kdb.DeviceClassDiode | \ 

66 kdb.DeviceClassMOS3Transistor | kdb.DeviceClassMOS4Transistor: 

67 pass 

68 

69 # create capacitor device class 

70 cap = kdb.DeviceClassCapacitor() 

71 # cap.name = 'KPEX_CAP' 

72 cap.name = 'C' 

73 cap.description = "Extracted by KPEX/2.5D" 

74 expanded_netlist.add(cap) 

75 

76 # create resistor device class 

77 res = kdb.DeviceClassResistor() 

78 # res.name = 'KPEX_RES' 

79 res.name = 'R' 

80 res.description = "Extracted by KPEX/2.5D" 

81 expanded_netlist.add(res) 

82 

83 fc_gnd_net = top_circuit.create_net('FC_GND') # create GROUND net 

84 vsubs_net = top_circuit.create_net("VSUBS") 

85 

86 summary = extraction_results.summarize() 

87 cap_items = sorted(summary.capacitances.items()) 

88 res_items = sorted(summary.resistances.items()) 

89 

90 # build table: name -> net 

91 name2net: Dict[str, kdb.Net] = {n.expanded_name(): n for n in top_circuit.each_net()} 

92 

93 def add_net_if_needed(net_name: str): 

94 if net_name in name2net: 

95 return 

96 name2net[net_name] = top_circuit.create_net(net_name) 

97 

98 # add additional nets for new nodes (e.g. created during R extraction of vias) 

99 for key, _ in cap_items: 

100 add_net_if_needed(key.net1) 

101 add_net_if_needed(key.net2) 

102 for key, _ in res_items: 

103 add_net_if_needed(key.net1) 

104 add_net_if_needed(key.net2) 

105 

106 for idx, (key, cap_value) in enumerate(cap_items): 

107 net1 = name2net[key.net1] 

108 net2 = name2net[key.net2] 

109 

110 c: kdb.Device = top_circuit.create_device(cap, f"ext_{idx+1}") 

111 c.connect_terminal('A', net1) 

112 c.connect_terminal('B', net2) 

113 c.set_parameter('C', cap_value) 

114 if net1 == net2: 

115 warning(f"Invalid attempt to create cap {c.name} between " 

116 f"same net {net1} with value {'%.12g' % cap_value}") 

117 

118 for idx, (key, res_value) in enumerate(res_items): 

119 net1 = name2net[key.net1] 

120 net2 = name2net[key.net2] 

121 

122 r: kdb.Device = top_circuit.create_device(res, f"ext_{idx+1}") 

123 r.connect_terminal('A', net1) 

124 r.connect_terminal('B', net2) 

125 r.set_parameter('R', res_value) 

126 if net1 == net2: 

127 warning(f"Invalid attempt to create resistor {r.name} between " 

128 f"same net {net1} with value {'%.12g' % res_value}") 

129 

130 return expanded_netlist