Coverage for klayout_pex/rcx25/c/overlap_extractor.py: 98%

64 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-02 23:32 +0000

1#! /usr/bin/env python3 

2# 

3# -------------------------------------------------------------------------------- 

4# SPDX-FileCopyrightText: 2024-2025 Martin Jan Köhler and Harald Pretl 

5# Johannes Kepler University, Institute for Integrated Circuits. 

6# 

7# This file is part of KPEX  

8# (see https://github.com/iic-jku/klayout-pex). 

9# 

10# This program is free software: you can redistribute it and/or modify 

11# it under the terms of the GNU General Public License as published by 

12# the Free Software Foundation, either version 3 of the License, or 

13# (at your option) any later version. 

14# 

15# This program is distributed in the hope that it will be useful, 

16# but WITHOUT ANY WARRANTY; without even the implied warranty of 

17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

18# GNU General Public License for more details. 

19# 

20# You should have received a copy of the GNU General Public License 

21# along with this program. If not, see <http://www.gnu.org/licenses/>. 

22# SPDX-License-Identifier: GPL-3.0-or-later 

23# -------------------------------------------------------------------------------- 

24# 

25 

26import klayout.db as kdb 

27 

28from klayout_pex.log import ( 

29 info, 

30) 

31from klayout_pex.tech_info import TechInfo 

32 

33from klayout_pex.rcx25.c.device_capacitor_plates import DeviceCapacitorPlates 

34from klayout_pex.rcx25.types import PolygonNeighborhood 

35from klayout_pex.rcx25.extraction_results import * 

36from klayout_pex.rcx25.extraction_reporter import ExtractionReporter 

37 

38 

39class OverlapExtractor: 

40 def __init__(self, 

41 all_layer_names: List[LayerName], 

42 layer_regions_by_name: Dict[LayerName, kdb.Region], 

43 dbu: float, 

44 tech_info: TechInfo, 

45 device_capacitor_plates: DeviceCapacitorPlates, 

46 results: CellExtractionResults, 

47 report: ExtractionReporter): 

48 self.all_layer_names = all_layer_names 

49 self.layer_regions_by_name = layer_regions_by_name 

50 self.dbu = dbu 

51 self.tech_info = tech_info 

52 self.device_capacitor_plates = device_capacitor_plates 

53 self.results = results 

54 self.report = report 

55 

56 def extract(self): 

57 for idx, (layer_name, layer_region) in enumerate(self.layer_regions_by_name.items()): 

58 ovl_visitor = self.PEXPolygonNeighborhoodVisitor( 

59 layer_names=self.all_layer_names, 

60 inside_layer_index=idx, 

61 dbu=self.dbu, 

62 tech_info=self.tech_info, 

63 device_capacitor_plates=self.device_capacitor_plates, 

64 results=self.results, 

65 report=self.report 

66 ) 

67 

68 # See comment above: as we use the layers in the stack order, 

69 # the input index is also the metal layer index 

70 ovl_children = [kdb.CompoundRegionOperationNode.new_secondary(r) 

71 for r in self.layer_regions_by_name.values()] 

72 

73 # We don't use a distance - hence only true overlaps will be considered 

74 ovl_node = kdb.CompoundRegionOperationNode.new_polygon_neighborhood(ovl_children, ovl_visitor) 

75 

76 layer_region.complex_op(ovl_node) 

77 

78 class PEXPolygonNeighborhoodVisitor(kdb.PolygonNeighborhoodVisitor): 

79 def __init__(self, 

80 layer_names: List[LayerName], 

81 inside_layer_index: int, 

82 dbu: float, 

83 tech_info: TechInfo, 

84 device_capacitor_plates: DeviceCapacitorPlates, 

85 results: CellExtractionResults, 

86 report: ExtractionReporter): 

87 super().__init__() 

88 self.layer_names = layer_names 

89 self.inside_layer_index = inside_layer_index 

90 self.dbu = dbu 

91 self.tech_info = tech_info 

92 self.device_capacitor_plates = device_capacitor_plates 

93 self.results = results 

94 self.report = report 

95 

96 def neighbors(self, 

97 layout: kdb.Layout, 

98 cell: kdb.Cell, 

99 polygon: kdb.PolygonWithProperties, 

100 neighborhood: PolygonNeighborhood): 

101 # We just look "upwards", as we don't want to count areas twice 

102 

103 shielded_region = kdb.Region() 

104 bottom_region = kdb.Region(polygon) 

105 bot_layer_name = self.layer_names[self.inside_layer_index] 

106 net_bot = polygon.property('net') # NOTE: the substrate's is the one of its well or VSUBS 

107 

108 for other_layer_index in range(self.inside_layer_index + 1, len(self.layer_names)): 

109 polygons_above = neighborhood.get(other_layer_index, None) 

110 if polygons_above is None: 

111 continue 

112 

113 for polygon_above in polygons_above: 

114 net_top = polygon_above.property('net') 

115 

116 if net_top == net_bot: 

117 # NOTE: e.g. VDD over its nwell, which still shields the nwell from the shapes above 

118 if bot_layer_name == self.tech_info.internal_substrate_layer_name: 

119 shielded_region.insert(polygon_above) 

120 continue 

121 

122 top_layer_name = self.layer_names[other_layer_index] 

123 

124 # NOTE: the capacitance of a device, which its model has (e.g. the junction of the diffusion 

125 # and the substrate), but the shape above shields (e.g. the substrate from the metal above) 

126 if self.tech_info.is_device_capacitance(top_layer_name, bot_layer_name): 

127 shielded_region.insert(polygon_above) 

128 continue 

129 

130 # NOTE: RCX25Extractor checked that the tech info has the overlap capacitance of each layer pair 

131 overlap_cap_spec = self.tech_info.overlap_cap_by_layer_names[top_layer_name][bot_layer_name] 

132 

133 top_region = kdb.Region(polygon_above) 

134 

135 overlap_area = top_region.__and__(bottom_region) - shielded_region 

136 

137 overlap_area_um2 = overlap_area.area() * self.dbu ** 2 

138 cap_femto = overlap_area_um2 * overlap_cap_spec.capacitance / 1000.0 

139 # info(f"(Overlap): {top_layer_name}({net_top})-{bot_layer_name}({net_bot}): " 

140 # f"cap: {round(cap_femto, 2)} fF, " 

141 # f"area: {overlap_area_um2} µm^2") 

142 

143 # NOTE: the model of a device has the capacitance between its plates, 

144 # but the plate above still shields the one below from the layers further above 

145 if self.device_capacitor_plates.are_plates_of_a_device(polygon, net_bot, polygon_above, net_top): 

146 cap_femto = 0.0 

147 

148 if cap_femto > 0.0: 

149 ovk = OverlapKey(layer_top=top_layer_name, 

150 net_top=net_top, 

151 layer_bot=bot_layer_name, 

152 net_bot=net_bot) 

153 cap = OverlapCap(key=ovk, 

154 cap_value=cap_femto, 

155 shielded_area=0.0, # TODO shielded_area_um2, 

156 unshielded_area=0.0, # TODO unshielded_area_um2, 

157 tech_spec=overlap_cap_spec) 

158 

159 self.results.add_overlap_cap(cap) 

160 

161 self.report.output_overlap(overlap_cap=cap, 

162 bottom_polygon=polygon, 

163 top_polygon=polygon_above, 

164 overlap_area=overlap_area) 

165 

166 shielded_region.insert(polygon_above)