Coverage for klayout_pex/rcx25/c/overlap_extractor.py: 90%
62 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-03-31 19:36 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-03-31 19:36 +0000
1#! /usr/bin/env python3
2#
3# --------------------------------------------------------------------------------
4# SPDX-FileCopyrightText: 2024 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/martinjankoehler/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#
26import klayout.db as kdb
28from klayout_pex.log import (
29 info,
30 warning,
31)
32from klayout_pex.tech_info import TechInfo
34from klayout_pex.rcx25.types import PolygonNeighborhood
35from klayout_pex.rcx25.extraction_results import *
36from klayout_pex.rcx25.extraction_reporter import ExtractionReporter
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 results: CellExtractionResults,
46 report: ExtractionReporter):
47 self.all_layer_names = all_layer_names
48 self.layer_regions_by_name = layer_regions_by_name
49 self.dbu = dbu
50 self.tech_info = tech_info
51 self.results = results
52 self.report = report
54 def extract(self):
55 for idx, (layer_name, layer_region) in enumerate(self.layer_regions_by_name.items()):
56 ovl_visitor = self.PEXPolygonNeighborhoodVisitor(
57 layer_names=self.all_layer_names,
58 inside_layer_index=idx,
59 dbu=self.dbu,
60 tech_info=self.tech_info,
61 results=self.results,
62 report=self.report
63 )
65 # See comment above: as we use the layers in the stack order,
66 # the input index is also the metal layer index
67 ovl_children = [kdb.CompoundRegionOperationNode.new_secondary(r)
68 for r in self.layer_regions_by_name.values()]
70 # We don't use a distance - hence only true overlaps will be considered
71 ovl_node = kdb.CompoundRegionOperationNode.new_polygon_neighborhood(ovl_children, ovl_visitor)
73 layer_region.complex_op(ovl_node)
75 class PEXPolygonNeighborhoodVisitor(kdb.PolygonNeighborhoodVisitor):
76 def __init__(self,
77 layer_names: List[LayerName],
78 inside_layer_index: int,
79 dbu: float,
80 tech_info: TechInfo,
81 results: CellExtractionResults,
82 report: ExtractionReporter):
83 super().__init__()
84 self.layer_names = layer_names
85 self.inside_layer_index = inside_layer_index
86 self.dbu = dbu
87 self.tech_info = tech_info
88 self.results = results
89 self.report = report
91 def neighbors(self,
92 layout: kdb.Layout,
93 cell: kdb.Cell,
94 polygon: kdb.PolygonWithProperties,
95 neighborhood: PolygonNeighborhood):
96 # We just look "upwards", as we don't want to count areas twice
98 shielded_region = kdb.Region()
99 bottom_region = kdb.Region(polygon)
100 bot_layer_name = self.layer_names[self.inside_layer_index]
101 net_bot = self.tech_info.internal_substrate_layer_name \
102 if bot_layer_name == self.tech_info.internal_substrate_layer_name \
103 else polygon.property('net')
105 for other_layer_index in range(self.inside_layer_index + 1, len(self.layer_names)):
106 polygons_above = neighborhood.get(other_layer_index, None)
107 if polygons_above is None:
108 continue
110 for polygon_above in polygons_above:
111 net_top = polygon_above.property('net')
113 if net_top == net_bot:
114 continue
116 top_layer_name = self.layer_names[other_layer_index]
118 top_overlap_specs = self.tech_info.overlap_cap_by_layer_names.get(top_layer_name, None)
119 if not top_overlap_specs:
120 warning(f"No overlap cap specified for layer top={top_layer_name}")
121 return
122 overlap_cap_spec = top_overlap_specs.get(bot_layer_name, None)
123 if not overlap_cap_spec:
124 warning(f"No overlap cap specified for layer bottom={bot_layer_name}")
125 return
127 top_region = kdb.Region(polygon_above)
129 overlap_area = top_region.__and__(bottom_region) - shielded_region
131 overlap_area_um2 = overlap_area.area() * self.dbu ** 2
132 cap_femto = overlap_area_um2 * overlap_cap_spec.capacitance / 1000.0
133 info(f"(Overlap): {top_layer_name}({net_top})-{bot_layer_name}({net_bot}): "
134 f"cap: {round(cap_femto, 2)} fF, "
135 f"area: {overlap_area_um2} µm^2")
137 if cap_femto > 0.0:
138 ovk = OverlapKey(layer_top=top_layer_name,
139 net_top=net_top,
140 layer_bot=bot_layer_name,
141 net_bot=net_bot)
142 cap = OverlapCap(key=ovk,
143 cap_value=cap_femto,
144 shielded_area=0.0, # TODO shielded_area_um2,
145 unshielded_area=0.0, # TODO unshielded_area_um2,
146 tech_spec=overlap_cap_spec)
148 self.results.add_overlap_cap(cap)
150 self.report.output_overlap(overlap_cap=cap,
151 bottom_polygon=polygon,
152 top_polygon=polygon_above,
153 overlap_area=overlap_area)
155 shielded_region.insert(polygon_above)